Ticket Crons
Automated background jobs that manage ticket lifecycle transitions, client notifications, and CSAT survey delivery for support tickets.
Overview
The Ticket Crons module contains three scheduled jobs that automate different stages of the ticket lifecycle. These crons handle scenarios where tickets require automatic status changes, client follow-up reminders, or internal team alerts — all without manual intervention from the support team.
The three crons work together to ensure tickets don't remain in limbo: incomplete tickets get reminders and eventually auto-close, delivered-shipment tickets auto-resolve, and follow-up tickets trigger internal SLA alerts.
Key Concepts
| Concept | Description |
|---|---|
| Incomplete Ticket | A ticket the client started creating but never finished submitting (status = INCOMPLETE) |
| Follow-Up Ticket | A ticket awaiting a response from the support team for more than 24 hours (status = FOLLOW_UP) |
| Autoclose | Automatic resolution of tickets whose associated shipment was delivered, making the ticket no longer relevant |
| CSAT | Customer Satisfaction survey sent via email, WhatsApp, and in-platform notification after a ticket is closed |
| Claim Types | Ticket types that involve a formal claim: Lost, Theft, Damaged, Overweight, Irregular Package |
| Delivery Types | Ticket types auto-closed on delivery: Shipment Created Without Changes, Delay, Redirection, Delivery Attempt |
Cron Summary
| Cron | Route | Trigger | What It Does |
|---|---|---|---|
notifyFollowUpAndIncompleteTickets | GET /notify/tickets/follow-up-incomplete | Periodic | Sends reminders to clients with incomplete tickets; sends Slack SLA alerts for follow-up tickets |
notifyAutocloseTickets | GET /notify/tickets/autoclose | Periodic | Auto-accepts tickets whose shipment was delivered and sends CSAT surveys |
closeTicketsIncomplete | GET /notify/tickets/incomplete | Periodic | Auto-declines claim tickets that remained incomplete for 10+ days and sends CSAT surveys |
Data Flow
Cron 1: notifyFollowUpAndIncompleteTickets
Cron 2: notifyAutocloseTickets
Cron 3: closeTicketsIncomplete
Detailed Behavior
notifyFollowUpAndIncompleteTickets
Query criteria:
- Ticket status is
INCOMPLETE(4) orFOLLOW_UP(5) - Created on or after 2025-05-12
- Company locale is not India (
locale_id ≠ 11)
For incomplete tickets, the cron sends a multi-channel reminder:
| Channel | Content |
|---|---|
Personalized message with ticket ID, ticket type, and a link to /settings/tickets | |
| Platform notification | Global notification with the last admin comment as context |
| Ticket comment | Personalized message per ticket type (e.g., "Your lost package claim is incomplete") |
| Notification via queue with ticket status context |
The personalized message varies by ticket type using translation keys:
| Ticket Type | Translation Key |
|---|---|
| Delay | notification.ticket.incomplete.delay |
| Redirection | notification.ticket.incomplete.redirection |
| Wrong Address | notification.ticket.incomplete.wrong_address |
| Shipment Created Without Changes | notification.ticket.incomplete.shipment_created_without_changes |
| Overweight | notification.ticket.incomplete.overweight |
| Lost | notification.ticket.incomplete.lost |
| Damaged | notification.ticket.incomplete.damaged |
| Theft | notification.ticket.incomplete.theft |
For follow-up tickets, the cron sends a Slack alert to #alert-cs-tickets with:
- Company name, ticket type, ticket status
- Time without attention indicator ("+24 hours")
- Last client comment
- SLA compliance message
notifyAutocloseTickets
Query criteria:
- Ticket type is one of: Shipment Created Without Changes (12), Delay (8), Redirection (14), Delivery Attempt (25)
- Ticket status is NOT Accepted, Declined, or Claim In Review
- Associated shipment status is Delivered or Delivered at Origin
Actions per ticket:
- Updates ticket status to
ACCEPTED(2) - Inserts a translated "delivered" comment
- Records status change in ticket history
- Sends CSAT notifications (platform rating + email + WhatsApp)
closeTicketsIncomplete
Query criteria:
- Ticket status is
INCOMPLETE(4) - Last updated 10+ days ago (
utc_updated_at ≤ UTC_TIMESTAMP() - INTERVAL 10 DAY) - Ticket type is a claim type: Lost (4), Theft (13), Damaged (5), Overweight (3), Irregular Package (21)
- Company locale is not India (
locale_id ≠ 11)
Actions:
- Bulk updates all matching tickets to
DECLINED(3) - Batch inserts history records for all tickets
- For each ticket individually:
- Looks up the user to notify (ticket creator or company owner)
- Generates a personalized close message per ticket type
- Inserts a ticket comment with the close message
- Sends CSAT notifications (platform rating + email + WhatsApp)
The close message varies by ticket type using translation keys:
| Ticket Type | Translation Key |
|---|---|
| Delay | notification.ticket.in_analysis.delay |
| Redirection | notification.ticket.in_analysis.redirection |
| Wrong Address | notification.ticket.in_analysis.wrong_address |
| Shipment Created Without Changes | notification.ticket.in_analysis.shipment_created_without_changes |
| Overweight | notification.ticket.in_analysis.overweight |
| Lost | notification.ticket.in_analysis.lost |
| Damaged | notification.ticket.in_analysis.damaged |
| Theft | notification.ticket.in_analysis.theft |
If the ticket type has no specific message, a generic fallback is used: notification.ticket.in_analysis.content_platform.
CSAT Notification Pipeline
When a ticket is closed (by autoclose or auto-decline), the sendTicketCsatNotifications function dispatches three notifications in parallel:
India locale (locale_id = 11) is excluded from all CSAT notifications.
Database
Tables
| Table | Purpose |
|---|---|
company_tickets | Main ticket table with status, type, company, shipment, and timestamps |
company_ticket_history | Audit log of all ticket status changes |
company_ticket_comments | Comments on tickets (client and admin messages) |
catalog_ticket_types | Ticket type catalog with descriptions and translation tags |
catalog_ticket_statuses | Ticket status catalog with slugs and descriptions |
companies | Company data including locale |
locales | Locale configuration with language and currency |
shipments | Shipment data used for autoclose delivery checks |
Key Relationships
Key Decisions
| Decision | Reasoning | Alternatives Considered |
|---|---|---|
setImmediate for all crons | Returns HTTP 200 immediately to the cron scheduler, then processes tickets asynchronously to avoid timeouts on large batches | Synchronous processing (risk of HTTP timeout), Bull queue (unnecessary complexity for periodic jobs) |
Bulk update + batch insert for closeTicketsIncomplete | Efficient single SQL statements for status change and history instead of N individual updates | Individual updates per ticket (slower, more DB round-trips) |
Promise.allSettled for notifications | One notification failure should not prevent others from being sent | Promise.all (one failure cancels all), sequential (slower) |
| India locale exclusion | India operations have a separate CSAT process; platform CSAT should not overlap | No exclusion (duplicate surveys), config-based exclusion (over-engineering) |
| Personalized messages per ticket type | Each claim type has a different context; generic messages reduce client understanding | Single generic message (less helpful), per-ticket custom text (not scalable) |
| Translation keys stored in DB | Content team can update message wording without code changes | Hardcoded strings (requires deployments for text changes) |
Dependencies
- Internal:
ticketsV2.util.js(notification logic, message generation),companies.util.js(ticket comments, rating notifications),notificationsservice (email, WhatsApp, Slack, platform),translate.util.js(i18n) - External: Mailgun (email delivery), WhatsApp queue (message delivery), Slack API (team alerts)
Related Documentation
- API Endpoints — Cron route definitions and authentication
- User Guide — Non-technical overview of ticket automation
