๐ Account Active Status Check
๐ Overviewโ
The Account Active Status Check job is a weekly maintenance task that identifies inactive software accounts and marks them for purging. It examines all active main accounts, checks their software subscription status, and flags accounts with canceled subscriptions (over 14 days) or no subscriptions for future cleanup. The job processes accounts in batches of 500 to optimize performance.
Complete Flow:
- Cron Initialization:
queue-manager/crons/accounts/checkaccountactivestatus.js - Service Processing:
queue-manager/services/accounts/accountpurgecheck.js - Queue Definition: None (direct database operations)
Execution Pattern: Cron-based (weekly on Sundays at midnight)
Queue Name: N/A (no Bull queue, service-only)
Environment Flag: QM_ACCOUNTS_PURGECHECK=true (in index.js)
๐ Complete Processing Flowโ
sequenceDiagram
participant CRON as Cron Schedule<br/>(Weekly Sunday)
participant SERVICE as Purge Check<br/>Service
participant ACCOUNT_DB as Accounts<br/>Collection
participant SUB_DB as Store<br/>Subscriptions
participant LOGGER as Logger
CRON->>SERVICE: Weekly status check
SERVICE->>LOGGER: Start purge check process
loop Process in batches of 500
SERVICE->>ACCOUNT_DB: Query active main accounts<br/>(without purge.lead flag)
ACCOUNT_DB-->>SERVICE: Return batch of accounts
alt Batch is empty
SERVICE->>LOGGER: No more accounts
SERVICE->>SERVICE: Exit loop
else Process batch
loop For each account
SERVICE->>SUB_DB: Get latest software subscription
SUB_DB-->>SERVICE: Return subscription
alt No subscription found
SERVICE->>ACCOUNT_DB: Set purge.lead = true
SERVICE->>LOGGER: Mark for purging<br/>(no subscription)
else Subscription canceled > 14 days
SERVICE->>ACCOUNT_DB: Set purge.lead = true
SERVICE->>LOGGER: Mark for purging<br/>(X days since cancel)
else Still active
SERVICE->>LOGGER: Keep active
end
end
SERVICE->>LOGGER: Batch summary<br/>(active vs. inactive)
end
end
SERVICE->>LOGGER: Final summary<br/>(total processed, counts)
SERVICE-->>CRON: Return results
๐ Source Filesโ
1. Cron Initializationโ
File: queue-manager/crons/accounts/checkaccountactivestatus.js
Purpose: Schedule weekly account purge checks
Cron Pattern: 0 0 * * 0 (every Sunday at midnight)
Initialization:
const { isSoftwareActive } = require('../../services/accounts/accountpurgecheck');
const cron = require('node-cron');
const logger = require('../../utilities/logger');
let inProgress = false;
exports.start = async () => {
try {
cron.schedule('0 0 * * 0', async () => {
if (!inProgress) {
inProgress = true;
await isSoftwareActive();
inProgress = false;
}
});
} catch (err) {
logger.error({ initiator: 'QM/accounts/purgecheck', error: err });
}
};
In-Progress Lock: Prevents overlapping executions if processing takes longer than 1 week.
2. Service Processing (THE CORE LOGIC)โ
File: queue-manager/services/accounts/accountpurgecheck.js
Purpose: Check account subscription status and mark inactive accounts for purging
Key Functions:
- Query active main accounts without purge flag
- Batch processing (500 accounts per batch)
- Check latest software subscription for each account
- Calculate days since subscription cancellation
- Mark inactive accounts with
metadata.purge.lead = true - Comprehensive logging of results
Main Processing Function:
const isSoftwareActive = async () => {
try {
const batchSize = 500; // Process 500 accounts at a time
let skip = 0;
let allInactiveAccounts = [];
let allStillActiveAccounts = [];
let totalProcessed = 0;
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: 'Starting account purge check process...',
});
while (true) {
// Step 1: Get active main accounts without purge flag
const activeAccounts = await Account.find({
main: true,
active: true,
$or: [{ 'metadata.purge.lead': { $exists: false } }, { 'metadata.purge.lead': false }],
})
.sort({ _id: 1 }) // Consistent sorting for pagination
.skip(skip)
.limit(batchSize)
.lean();
// Exit when no more accounts
if (activeAccounts?.length === 0) {
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: 'No more accounts to process',
});
break;
}
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Processing batch: ${skip + 1} to ${skip + activeAccounts.length}`,
});
const accountIds = activeAccounts.map(account => account._id.toString());
let inactiveAccounts = [];
let stillActiveAccounts = [];
// Current timestamp and 14-day threshold
const currentTimestamp = Math.floor(Date.now() / 1000);
const fourteenDaysInSeconds = 14 * 24 * 60 * 60; // 14 days in seconds
// Step 2: Check each account's subscription status
for (const accountId of accountIds) {
// Get latest software subscription
const latestSubscription = await StoreSubscription.findOne({
$or: [
{ 'metadata.account_id': accountId },
{ 'metadata.account_id': new mongoose.Types.ObjectId(accountId) },
],
'plan.metadata.software': 'true',
'plan.metadata.product_type': 'software',
})
.sort({ created_at: -1 })
.lean(); // Get the latest subscription
if (latestSubscription) {
// Step 3: Check if subscription is canceled and over 14 days
if (
latestSubscription.status === 'canceled' &&
latestSubscription.canceled_at &&
currentTimestamp - latestSubscription.canceled_at > fourteenDaysInSeconds
) {
// Mark account as inactive
try {
await Account.updateOne(
{ _id: accountId },
{ $set: { 'metadata.purge.lead': true } },
);
inactiveAccounts.push({
accountId: accountId,
subscriptionStatus: latestSubscription.status,
subscriptionId: latestSubscription._id,
planName: latestSubscription.plan?.nickname,
canceledAt: latestSubscription.canceled_at,
daysSinceCancellation: Math.floor(
(currentTimestamp - latestSubscription.canceled_at) / (24 * 60 * 60),
),
});
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Marked account ${accountId} for purging - canceled ${Math.floor(
(currentTimestamp - latestSubscription.canceled_at) / (24 * 60 * 60),
)} days ago`,
});
} catch (updateError) {
logger.error({
initiator: 'QM/accounts/accountpurgecheck',
error: updateError,
message: `Error updating account ${accountId}`,
});
}
} else {
// Subscription still active or recently canceled
stillActiveAccounts.push({
accountId: accountId,
subscriptionStatus: latestSubscription.status,
subscriptionId: latestSubscription._id,
planName: latestSubscription.plan?.nickname,
});
}
} else {
// No software subscription found - mark as inactive
try {
await Account.updateOne({ _id: accountId }, { $set: { 'metadata.purge.lead': true } });
inactiveAccounts.push({
accountId: accountId,
subscriptionStatus: 'no_software_subscription',
subscriptionId: null,
planName: null,
canceledAt: null,
daysSinceCancellation: null,
});
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Marked account ${accountId} for purging - no software subscription found`,
});
} catch (updateError) {
logger.error({
initiator: 'QM/accounts/accountpurgecheck',
error: updateError,
message: `Error updating account ${accountId} (no subscription)`,
});
}
}
}
// Accumulate results from this batch
allInactiveAccounts = allInactiveAccounts.concat(inactiveAccounts);
allStillActiveAccounts = allStillActiveAccounts.concat(stillActiveAccounts);
totalProcessed += accountIds.length;
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Batch ${Math.floor(skip / batchSize) + 1} completed: ${
stillActiveAccounts.length
} still active, ${
inactiveAccounts.length
} marked for purging. Total processed: ${totalProcessed}`,
});
// Increment skip for next batch
skip += batchSize;
}
// Final summary
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Final Summary: Total processed: ${totalProcessed}, Still active: ${allStillActiveAccounts.length}, Marked for purging: ${allInactiveAccounts.length}`,
});
// Log all inactive accounts
if (allInactiveAccounts.length > 0) {
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `All accounts marked for purging: ${allInactiveAccounts
.map(acc => `${acc.accountId} (${acc.daysSinceCancellation} days)`)
.join(', ')}`,
});
}
return {
inactiveAccounts: allInactiveAccounts,
activeAccounts: allStillActiveAccounts,
summary: {
total: totalProcessed,
active: allStillActiveAccounts.length,
inactive: allInactiveAccounts.length,
markedForPurging: allInactiveAccounts.length,
},
};
} catch (error) {
logger.error({
initiator: 'QM/accounts/accountpurgecheck',
error: error,
});
return {
inactiveAccounts: [],
activeAccounts: [],
summary: { total: 0, active: 0, inactive: 0, markedForPurging: 0 },
};
}
};
module.exports = {
isSoftwareActive,
};
๐๏ธ Collections Usedโ
accountsโ
- Operations: Read, Update
- Model:
shared/models/account.js - Usage Context:
- Query active main accounts
- Mark inactive accounts with purge flag
- Track purge status via
metadata.purge.lead
Query Criteria:
{
main: true, // Main accounts only
active: true, // Active accounts
$or: [
{ 'metadata.purge.lead': { $exists: false } }, // Never checked
{ 'metadata.purge.lead': false }, // Previously active
],
}
Update Operation:
{
$set: { 'metadata.purge.lead': true } // Mark for purging
}
Key Fields:
main: Boolean - identifies main/parent accountsactive: Boolean - account active statusmetadata.purge.lead: Boolean - purge flag for inactive accounts
store_subscriptionsโ
- Operations: Read
- Model:
shared/models/store-subscription.js - Usage Context: Check latest software subscription status for each account
Query Criteria:
{
$or: [
{ 'metadata.account_id': accountId }, // String format
{ 'metadata.account_id': ObjectId(accountId) }, // ObjectId format
],
'plan.metadata.software': 'true', // Software product
'plan.metadata.product_type': 'software', // Product type
}
Sort: { created_at: -1 } (latest subscription first)
Key Fields:
status: 'active' | 'canceled' | 'past_due' | etc.canceled_at: Unix timestamp (seconds) when subscription canceledplan.nickname: Subscription plan nameplan.metadata.software: 'true' for software subscriptionsplan.metadata.product_type: 'software'metadata.account_id: Associated account ID
๐ง Job Configurationโ
Cron Scheduleโ
'0 0 * * 0'; // Every Sunday at midnight (00:00)
Frequency Rationale: Weekly checks provide timely identification of inactive accounts while minimizing database load.
Batch Processingโ
const batchSize = 500; // Process 500 accounts per batch
Performance Optimization:
- Prevents memory overflow from large result sets
- Enables progress tracking via logging
- Allows graceful handling of connection issues
Inactivity Thresholdโ
const fourteenDaysInSeconds = 14 * 24 * 60 * 60; // 14 days
Grace Period: Accounts with subscriptions canceled less than 14 days ago are NOT marked for purging.
๐ Processing Logic - Detailed Flowโ
Account Selection Criteriaโ
Main Accounts Only:
{
main: true;
}
- Excludes sub-accounts (which inherit parent status)
- Focuses on parent/master accounts
Active Accounts Only:
{
active: true;
}
- Skips already-deactivated accounts
- Only checks currently active accounts
Without Purge Flag:
{
$or: [
{ 'metadata.purge.lead': { $exists: false } },
{ 'metadata.purge.lead': false },
],
}
- Avoids re-processing already-marked accounts
- Includes accounts never checked before
Subscription Status Logicโ
Case 1: No Software Subscription
if (!latestSubscription) {
// Mark for purging immediately
await Account.updateOne({ _id: accountId }, { $set: { 'metadata.purge.lead': true } });
}
Reason: Account without software subscription should not exist as active.
Case 2: Subscription Canceled > 14 Days
if (
latestSubscription.status === 'canceled' &&
latestSubscription.canceled_at &&
currentTimestamp - latestSubscription.canceled_at > fourteenDaysInSeconds
) {
// Mark for purging
await Account.updateOne({ _id: accountId }, { $set: { 'metadata.purge.lead': true } });
}
Reason: Grace period expired, account should be purged.
Case 3: Still Active or Recently Canceled
else {
stillActiveAccounts.push({
accountId: accountId,
subscriptionStatus: latestSubscription.status,
subscriptionId: latestSubscription._id,
planName: latestSubscription.plan?.nickname,
});
}
Reason: Subscription active, trialing, past_due, or canceled within 14 days.
Batch Processing Flowโ
-
Query Batch of Accounts
const activeAccounts = await Account.find({...})
.sort({ _id: 1 })
.skip(skip)
.limit(batchSize)
.lean();- Consistent
_idsorting ensures no duplicates lean()improves performance (plain objects)
- Consistent
-
Check Each Account's Subscription
const latestSubscription = await StoreSubscription.findOne({...})
.sort({ created_at: -1 })
.lean();- Only fetches latest subscription
- Checks both string and ObjectId formats
-
Calculate Days Since Cancellation
const daysSinceCancellation = Math.floor(
(currentTimestamp - latestSubscription.canceled_at) / (24 * 60 * 60),
);- Converts seconds to days
- Used for logging and reporting
-
Update Account if Inactive
await Account.updateOne({ _id: accountId }, { $set: { 'metadata.purge.lead': true } });- Atomic update operation
- Sets purge flag for future cleanup jobs
-
Log Batch Results
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Batch ${Math.floor(skip / batchSize) + 1} completed: ${
stillActiveAccounts.length
} still active, ${
inactiveAccounts.length
} marked for purging. Total processed: ${totalProcessed}`,
}); -
Increment Skip for Next Batch
skip += batchSize; // Move to next 500 accounts
Return Value Structureโ
{
inactiveAccounts: [
{
accountId: '507f1f77bcf86cd799439011',
subscriptionStatus: 'canceled',
subscriptionId: '507f1f77bcf86cd799439012',
planName: 'Pro Plan',
canceledAt: 1735689600, // Unix timestamp
daysSinceCancellation: 20
},
{
accountId: '507f1f77bcf86cd799439013',
subscriptionStatus: 'no_software_subscription',
subscriptionId: null,
planName: null,
canceledAt: null,
daysSinceCancellation: null
}
],
activeAccounts: [
{
accountId: '507f1f77bcf86cd799439014',
subscriptionStatus: 'active',
subscriptionId: '507f1f77bcf86cd799439015',
planName: 'Enterprise Plan'
}
],
summary: {
total: 1500, // Total accounts processed
active: 1475, // Still active accounts
inactive: 25, // Marked for purging
markedForPurging: 25
}
}
๐จ Error Handlingโ
Common Error Scenariosโ
Account Update Failureโ
try {
await Account.updateOne({ _id: accountId }, { $set: { 'metadata.purge.lead': true } });
} catch (updateError) {
logger.error({
initiator: 'QM/accounts/accountpurgecheck',
error: updateError,
message: `Error updating account ${accountId}`,
});
}
Result: Error logged, processing continues with next account.
Subscription Query Failureโ
const latestSubscription = await StoreSubscription.findOne({...})
.sort({ created_at: -1 })
.lean();
if (!latestSubscription) {
// Treat as no subscription found
await Account.updateOne(
{ _id: accountId },
{ $set: { 'metadata.purge.lead': true } },
);
}
Result: Account marked for purging if no subscription exists.
Database Connection Errorโ
catch (error) {
logger.error({
initiator: 'QM/accounts/accountpurgecheck',
error: error,
});
return {
inactiveAccounts: [],
activeAccounts: [],
summary: { total: 0, active: 0, inactive: 0, markedForPurging: 0 },
};
}
Result: Returns empty results, will retry next week.
Error Recoveryโ
- Batch-Level: If a batch fails, already-processed batches are saved (no rollback)
- Account-Level: Individual account errors don't stop batch processing
- Service-Level: Top-level errors return empty results, job retries next week
๐ Monitoring & Loggingโ
Start Loggingโ
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: 'Starting account purge check process...',
});
Batch Loggingโ
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Processing batch: ${skip + 1} to ${skip + activeAccounts.length}`,
});
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Batch ${Math.floor(skip / batchSize) + 1} completed: ${
stillActiveAccounts.length
} still active, ${
inactiveAccounts.length
} marked for purging. Total processed: ${totalProcessed}`,
});
Account-Level Loggingโ
// Marked for purging (canceled)
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Marked account ${accountId} for purging - canceled ${Math.floor(
(currentTimestamp - latestSubscription.canceled_at) / (24 * 60 * 60),
)} days ago`,
});
// Marked for purging (no subscription)
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Marked account ${accountId} for purging - no software subscription found`,
});
Final Summary Loggingโ
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Final Summary: Total processed: ${totalProcessed}, Still active: ${allStillActiveAccounts.length}, Marked for purging: ${allInactiveAccounts.length}`,
});
// List all inactive accounts
if (allInactiveAccounts.length > 0) {
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `All accounts marked for purging: ${allInactiveAccounts
.map(acc => `${acc.accountId} (${acc.daysSinceCancellation} days)`)
.join(', ')}`,
});
}
Error Loggingโ
logger.error({
initiator: 'QM/accounts/accountpurgecheck',
error: updateError,
message: `Error updating account ${accountId}`,
});
logger.error({
initiator: 'QM/accounts/accountpurgecheck',
error: error,
});
Performance Metricsโ
- Average Processing Time: 5-15 minutes (depends on account count)
- Batch Size: 500 accounts per batch
- Typical Volume: 1,000-10,000 accounts per week
- Mark Rate: ~5-10% of accounts marked for purging
๐ Integration Pointsโ
Triggers This Jobโ
- Cron Schedule: Every Sunday at midnight automatically
- Manual Trigger: Via API endpoint (if QM_HOOKS=true)
Data Dependenciesโ
- Active Main Accounts: Must have
main: trueandactive: true - Store Subscriptions: Software subscription records with cancellation dates
- 14-Day Grace Period: Hardcoded threshold for purge eligibility
Jobs That Depend On Thisโ
- Account Purge Job: Uses
metadata.purge.leadflag to identify accounts for deletion - Cleanup Jobs: Additional cleanup tasks may reference purge flag
- Reporting Jobs: Analytics on subscription churn and account lifecycle
โ ๏ธ Important Notesโ
Side Effectsโ
- โ ๏ธ Account Flagging: Sets
metadata.purge.lead = trueon inactive accounts - โ ๏ธ Database Writes: Updates account records (1 write per inactive account)
- โ ๏ธ No Deletion: This job only FLAGS accounts, does NOT delete them
- โ ๏ธ Irreversible: Once flagged, accounts won't be re-checked (flag remains until purge or manual reset)
Performance Considerationsโ
- Weekly Schedule: Minimizes database load while catching inactive accounts
- Batch Processing: 500 accounts per batch prevents memory overflow
- Lean Queries: Uses
.lean()for improved performance (plain objects) - Sequential Processing: Accounts processed one-by-one within batch (avoids race conditions)
- Indexing: Ensure indexes on
accounts.main,accounts.active,accounts.metadata.purge.lead
Maintenance Notesโ
- Purge Flag Persistence:
metadata.purge.leadremains until account purged or manually reset - Grace Period: 14-day threshold hardcoded - requires code change to modify
- Subscription Types: Only checks
plan.metadata.product_type = 'software' - Main Accounts Only: Sub-accounts not checked (inherit parent status)
- Timestamp Format: Uses Unix timestamps (seconds), not milliseconds
Business Logicโ
Why 14 Days?
- Gives users time to renew subscriptions
- Prevents accidental purging of temporarily canceled accounts
- Industry standard grace period for SaaS
Why Mark Instead of Delete?
- Allows human review before permanent deletion
- Enables recovery if flagged incorrectly
- Separate purge job can handle actual deletion with additional safeguards
Why Weekly?
- Daily checks unnecessary (14-day grace period)
- Weekly schedule reduces database load
- Sunday midnight minimizes impact on business operations
๐งช Testingโ
Manual Triggerโ
# Via API (if QM_HOOKS=true)
POST http://localhost:6002/api/trigger/accounts/purgecheck
Create Test Scenariosโ
// Scenario 1: Account with canceled subscription (20 days ago)
const testAccount1 = await Account.create({
main: true,
active: true,
email: 'test1@example.com',
// No purge flag
});
const testSubscription1 = await StoreSubscription.create({
metadata: {
account_id: testAccount1._id.toString(),
},
plan: {
metadata: {
software: 'true',
product_type: 'software',
},
nickname: 'Pro Plan',
},
status: 'canceled',
canceled_at: Math.floor(Date.now() / 1000) - 20 * 24 * 60 * 60, // 20 days ago
created_at: Math.floor(Date.now() / 1000) - 60 * 24 * 60 * 60, // Created 60 days ago
});
// Scenario 2: Account with no subscription
const testAccount2 = await Account.create({
main: true,
active: true,
email: 'test2@example.com',
});
// Scenario 3: Account with recently canceled subscription (5 days ago)
const testAccount3 = await Account.create({
main: true,
active: true,
email: 'test3@example.com',
});
const testSubscription3 = await StoreSubscription.create({
metadata: {
account_id: testAccount3._id.toString(),
},
plan: {
metadata: {
software: 'true',
product_type: 'software',
},
nickname: 'Enterprise Plan',
},
status: 'canceled',
canceled_at: Math.floor(Date.now() / 1000) - 5 * 24 * 60 * 60, // 5 days ago
created_at: Math.floor(Date.now() / 1000) - 30 * 24 * 60 * 60,
});
// Scenario 4: Account with active subscription
const testAccount4 = await Account.create({
main: true,
active: true,
email: 'test4@example.com',
});
const testSubscription4 = await StoreSubscription.create({
metadata: {
account_id: testAccount4._id.toString(),
},
plan: {
metadata: {
software: 'true',
product_type: 'software',
},
nickname: 'Starter Plan',
},
status: 'active',
created_at: Math.floor(Date.now() / 1000) - 10 * 24 * 60 * 60,
});
// Run purge check
const result = await isSoftwareActive();
// Verify results
console.log('Summary:', result.summary);
// { total: 4, active: 2, inactive: 2, markedForPurging: 2 }
// Check flagged accounts
const flagged1 = await Account.findById(testAccount1._id);
console.log('Account 1 purge flag:', flagged1.metadata.purge.lead); // true (20 days)
const flagged2 = await Account.findById(testAccount2._id);
console.log('Account 2 purge flag:', flagged2.metadata.purge.lead); // true (no subscription)
const flagged3 = await Account.findById(testAccount3._id);
console.log('Account 3 purge flag:', flagged3.metadata?.purge?.lead); // undefined/false (only 5 days)
const flagged4 = await Account.findById(testAccount4._id);
console.log('Account 4 purge flag:', flagged4.metadata?.purge?.lead); // undefined/false (active)
Monitor Executionโ
// Count accounts pending purge check
const pendingCheck = await Account.countDocuments({
main: true,
active: true,
$or: [{ 'metadata.purge.lead': { $exists: false } }, { 'metadata.purge.lead': false }],
});
console.log('Accounts pending purge check:', pendingCheck);
// Count accounts marked for purging
const markedForPurge = await Account.countDocuments({
main: true,
'metadata.purge.lead': true,
});
console.log('Accounts marked for purging:', markedForPurge);
// Find accounts marked this week
const oneWeekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
const recentlyMarked = await Account.find({
'metadata.purge.lead': true,
updatedAt: { $gte: oneWeekAgo },
});
console.log('Recently marked accounts:', recentlyMarked.length);
// Check subscription distribution
const subscriptionStats = await StoreSubscription.aggregate([
{
$match: {
'plan.metadata.software': 'true',
'plan.metadata.product_type': 'software',
},
},
{
$group: {
_id: '$status',
count: { $sum: 1 },
},
},
]);
console.log('Subscription status distribution:', subscriptionStats);
// [ { _id: 'active', count: 500 }, { _id: 'canceled', count: 50 }, ... ]
Job Type: Scheduled
Execution Frequency: Weekly (Sundays at midnight)
Average Duration: 5-15 minutes
Status: Active