Flag HubSpot deals with missing fields and Slack the rep using Zapier

medium complexityCost: $20-50/mo

Prerequisites

Prerequisites
  • Zapier Professional plan (required for Schedule trigger + Webhooks + Code steps)
  • HubSpot private app token with crm.objects.deals.read scope
  • Slack workspace connected to Zapier
  • A mapping of HubSpot owner IDs to Slack user IDs

Step 1: Schedule a daily trigger

Create a new Zap with Schedule by Zapier:

  • Frequency: Every Day
  • Time: 7:00 AM

Step 2: Search for deals missing close date

Add a Webhooks by Zapier -> Custom Request step:

  • Method: POST
  • URL: https://api.hubapi.com/crm/v3/objects/deals/search
  • Headers: Authorization: Bearer YOUR_TOKEN, Content-Type: application/json
  • Data:
{
  "filterGroups": [{
    "filters": [
      {"propertyName": "closedate", "operator": "NOT_HAS_PROPERTY"},
      {"propertyName": "dealstage", "operator": "NOT_IN", "values": ["closedwon", "closedlost"]}
    ]
  }],
  "properties": ["dealname", "amount", "closedate", "dealstage", "hubspot_owner_id"],
  "limit": 100
}

Step 3: Search for deals missing amount

Add another Webhooks by Zapier -> Custom Request step with the same structure, swapping the filter:

{
  "filterGroups": [{
    "filters": [
      {"propertyName": "amount", "operator": "NOT_HAS_PROPERTY"},
      {"propertyName": "dealstage", "operator": "NOT_IN", "values": ["closedwon", "closedlost"]}
    ]
  }],
  "properties": ["dealname", "amount", "closedate", "dealstage", "hubspot_owner_id"],
  "limit": 100
}
Why two requests?

HubSpot's Search API uses AND logic within a filter group. You cannot OR two NOT_HAS_PROPERTY filters together. Two separate requests merged in the next step is the cleanest approach.

Step 4: Merge and group with Code by Zapier

Add a Code by Zapier step (JavaScript). Pass both webhook responses as input data:

const closeResp = JSON.parse(inputData.missingCloseRaw);
const amountResp = JSON.parse(inputData.missingAmountRaw);
 
const seen = new Set();
const allDeals = [];
 
for (const deal of [...(closeResp.results || []), ...(amountResp.results || [])]) {
  if (seen.has(deal.id)) continue;
  seen.add(deal.id);
  const props = deal.properties;
  const missing = [];
  if (!props.closedate) missing.push("close date");
  if (!props.amount) missing.push("amount");
 
  allDeals.push({
    id: deal.id,
    name: props.dealname,
    ownerId: props.hubspot_owner_id || "unassigned",
    missing: missing.join(", "),
  });
}
 
if (allDeals.length === 0) {
  return { count: 0, message: "" };
}
 
// Group by owner
const byOwner = {};
for (const deal of allDeals) {
  if (!byOwner[deal.ownerId]) byOwner[deal.ownerId] = [];
  byOwner[deal.ownerId].push(deal);
}
 
// Format one message per owner
const ownerMessages = Object.entries(byOwner).map(([ownerId, deals]) => {
  const lines = deals.map(d =>
    `- <https://app.hubspot.com/contacts/YOUR_PORTAL_ID/deal/${d.id}|${d.name}> -- missing *${d.missing}*`
  ).join("\n");
  return `${ownerId}:::${deals.length}:::${lines}`;
});
 
return { count: allDeals.length, owners: ownerMessages.join("|||") };
Zapier Code output limits

Code by Zapier can only return simple key-value pairs, not arrays. The script above encodes owner groups as a delimited string. Parse it in a downstream Looping step or use multiple Slack steps.

Step 5: Filter empty results

Add a Filter step:

  • Only continue if: count is greater than 0

Step 6: Send Slack messages per owner

Add a Looping by Zapier step to iterate over the |||-separated owner messages, then inside the loop add a Slack -> Send Direct Message step:

  • User: Map the HubSpot owner ID to the Slack user ID
  • Message:
*Missing Deal Fields*
You have deals with incomplete data:
 
{{lines from code output}}
 
Please update these deals today so forecasting stays accurate.

If owner-to-Slack mapping is too complex for your setup, replace the loop with a single Slack -> Send Channel Message step posting the full list to #sales-pipeline.

Step 7: Test and publish

  1. Test each step individually
  2. Verify the Slack DM formatting and links
  3. Turn the Zap on

Cost

  • Professional plan: $29.99/mo. Uses ~5-6 tasks per daily run (schedule + 2 webhooks + code + filter + Slack) = ~180 tasks/month.

Need help implementing this?

We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.