Remove Tag Automation Processor
Overview
The Remove Tag automation removes CRM tags from deals when they enter a specific pipeline stage. It validates tag existence, prevents errors on already-removed tags, and tracks automation status.
Source File: queue-manager/queues/deals/automations/remove-tag.js
Triggered By: queue-manager/services/deals/automations.js
Business Impact: MEDIUM - Deal organization
Processing Logic
sequenceDiagram
participant AUTO as Automation Service
participant QUEUE as Remove Tag Queue
participant DEAL as Deals Collection
participant TAG as CRM Tags
AUTO->>QUEUE: Add automation job
QUEUE->>TAG: Validate tag exists
TAG-->>QUEUE: Tag valid
QUEUE->>DEAL: Find eligible deals<br/>(stage + time window)
loop For Each Deal
QUEUE->>DEAL: Check if tag exists
alt Tag Present
QUEUE->>DEAL: Remove tag from deal.tags[]
QUEUE->>DEAL: Mark automation triggered
else Tag Not Present
QUEUE->>DEAL: Skip (already removed)
end
end
QUEUE->>AUTO: Update automation status
Key Features
Validation
- Tag ID Validation: Checks if tag is valid ObjectId
- Tag Existence: Verifies tag exists in account
- Association Check: Skips deals not having the tag
Deal Eligibility
Same as Add Tag automation:
stage_idmatches automation stageautomation_startsless than or equal to delayed dateautomation_startsgreater than or equal to automation creation date- Automation tracking checks
Core Code Pattern
// Validate tag
if (!tag || !mongoose.Types.ObjectId.isValid(tag)) {
throw new Error('Invalid tag id provided');
}
const validTag = await CrmTag.countDocuments({ _id: tag, account: accountID });
if (!validTag) {
throw new Error('Tag not found.');
}
// Find eligible deals
let dealData = await Deal.find({
stage_id: automation.stage_id,
$and: [
{ automation_starts: { $lte: delayedDate.toDate() } },
{ automation_starts: { $gte: automation.createdAt } },
],
$or: [
{ retry_automations: automationId },
{
$and: [
{ skip_automations: { $ne: automationId } },
{ automation_triggered: { $ne: automationId } },
],
},
],
}).exec();
// Remove tag from each deal
for (const deal of dealData) {
const tags = deal._doc.tags || [];
if (!tags.find(t => t.toString() === tag.toString())) {
throw new Error('Tag is already not associated.');
}
if (deal.tags) {
await Deal.updateOne({ _id: dealID }, { $pull: { tags: new mongoose.Types.ObjectId(tag) } });
}
}
Collections Used
Input: deal-automation
- Automation configuration
Validation: crm-tag
- Tag existence verification
Update: deal
- Remove tag from deal.tags array using
$pull - Update automation tracking fields
Use Cases
Example 1: Stage Progression
// Automation
{
stage_id: "closed_won",
action: "remove_tag",
data: { tag: "needs_follow_up" },
delay: 0
}
// Result: Remove follow-up tag when deal closes
Example 2: Cleanup on Rejection
// Automation
{
stage_id: "disqualified",
action: "remove_tag",
data: { tag: "qualified" },
delay: 0
}
// Result: Remove qualification tag when deal disqualified
Error Handling
Common Errors
- Invalid Tag ID: Not a valid ObjectId
- Tag Not Found: Tag doesn't exist in account
- Tag Not Associated: Deal doesn't have this tag
- Deal Not Found: Deal was deleted
Error Response
{
deal: dealID,
tag_id: ObjectId,
message: "Tag is already not associated.",
additional_info: {...}
}
Performance
Execution Time: 100-500ms per automation
Batch Size: All eligible deals in one run
Retry Strategy: 3 attempts with exponential backoff
Complexity: LOW
Lines of Code: 164