Eviworx
Docs

Reopen & Lifecycle Governance

Reopen governance uniformly controls whether, for how long, by whom and how often a closed entity may be reopened — across tickets, incidents and problems. The rules are the same for all three entity types and configurable per type in the lifecycle config. This also covers auto-close (time-based closing of resolved tickets), WC-auto-resolve (resolving unanswered waiting-customer tickets), stale reminders (inactivity escalation across all three domains), reopen escalation (a quality signal when something is reopened too often) and a dedicated analytics dashboard.

♻️
Features
✓ Uniform rules for ticket, incident, problem
✓ Configurable per entity type (lifecycle config)
✓ Dedicated permissions (reopen / reopenOverride)
✓ Reopen window in days and maximum count
✓ Mandatory reopen reason (+ optional note)
✓ Auto-close with advance warning (off by default)
✓ WC auto-resolve (WAITING_CUSTOMER → RESOLVED)
✓ Stale reminder (assignee → lead → manager)
✓ Reopen escalation above a threshold
✓ Lifecycle dashboard (reopen rate, top reasons)

🎫 For Agents: Automatic Ticket Actions

Three time-based automations act on tickets — all configurable and toggleable by an admin in system settings:

  • 1. WC auto-resolve (Waiting for Customer → Resolved): If the customer does not reply for X days, the ticket is automatically set to Resolved (code "Auto-Resolved — No Response"); optionally a warning goes to customer and agent Y days earlier. The timer runs from the last public message — whether from the customer or an agent: a reminder email to the customer restarts it. Internal notes and field changes do not count.
  • 2. Stale reminder (inactivity): Active tickets (Open, In Progress, Waiting for Support) left untouched too long trigger a reminder (staggered: assignee → group lead → manager). Here every change to the ticket resets the timer — status change, comment, field change, email reply. Status and SLA are never altered.
  • 3. Auto-close (Resolved → Closed): Tickets in Resolved are automatically closed after the configured deadline — also with an optional advance warning.

All deadlines live in the per-entity lifecycle config and are disabled by default. Technical details (config keys, notifications, jobs) below.

How a reopen works

A central check decides, based on the lifecycle config, whether a reopen is allowed. The system then resets the closing fields (including resolution code, closedAt/resolvedAt), increments reopenCount and writes a timeline entry, an audit entry and a notification.

Lifecycle-Config (per entity, admin-editable)
        │  reopenEnabled · reopenWindowDays · reopenWindowMode · maxReopenCount
        │  reasonRequired · notifyOnReopen · slaOnReopenFrom{Resolved,Closed}
        │  autoCloseEnabled · autoResolveWaitingEnabled · staleReminderEnabled · reopenEscalationThreshold
        ▼
Reopen check (window, limit, reason, approval)        │  → allowed / refused with error code        ▼
Reset per entity type   Ticket
   Incident (+ approval)
   Problem
        │  reset closedAt/resolvedAt/resolutionCode · reopenCount++ · reopenedAt
        ▼
   Audit REOPENED · visible timeline entry · Notification (*_REOPENED)

Reopen Evaluation (Policy Steps)

The check runs in a set order. reopenOverride bypasses ONLY window and limit — NOT the reason requirement and NOT the approval requirement (incident). Even an admin needs a reason.

Step Check Override? Error-Code
1Reopen enabled? (reopenEnabled)REOPEN_DISABLED
2Source terminal? (CLOSED/SPAM/…)REOPEN_NOT_TERMINAL
3Within reopen window? (reopenWindowDays from closedAt)✅ bypassedREOPEN_WINDOW_EXPIRED
4Below maximum? (reopenCount < maxReopenCount)✅ bypassedREOPEN_LIMIT_REACHED
5Valid reopen reason? (+ note if requiresNote)❌ always requiredREOPEN_REASON_REQUIRED / REOPEN_REASON_INVALID / REOPEN_NOTE_REQUIRED
6Approval needed? (incident only, on by default)❌ never bypassedREOPEN_REQUIRES_APPROVAL

onWindowExpired (Ticket): Once a ticket's reopen window has expired, a customer reply by email creates a new ticket linked to the old one (originContext EMAIL_FOLLOWUP) instead of reopening — the customer is never locked out.

reopenWindowMode: The reopen window counts either in calendar days (CALENDAR, default) or business days (BUSINESS). BUSINESS counts according to the business hours of the entity type's default SLA policy; if no default policy or business-hours config exists, it safely falls back to CALENDAR.

Behavior per Domain

Domain Source → target Reopen paths Approval
Ticket CLOSED/SPAMOPEN Manual (PATCH), email reply, web comment (system reopen)
Incident CLOSEDACKNOWLEDGED Dedicated route POST /:id/reopen Yes (default) — incidents.approveReopen
Problem CLOSED/RESOLVEDINVESTIGATING Manual (PATCH)

Important (ticket): RESOLVED is NOT a reopen source — moving from RESOLVED back to an active status is a normal editStatus change (no reason, no counter), keeping the reopen KPI clean. Reopen sources are exclusively CLOSED and SPAM. Reopening a merged ticket is redirected to the live target ticket (mergedToTicketId).

Feature Matrix per Domain

FeatureTicketProblemIncident
Reopen governance✅ (3 paths)✅ (manual)✅ (manual + approval)
Auto-Close
WC-Auto-Resolve
Stale-Reminder
Reopen escalation
Reopen window display
SLA config-driven

All lifecycle automations (auto-close, WC-auto-resolve, stale reminder, reopen escalation) are OFF by default and enabled per entity in the lifecycle config. The related jobs run on schedule but do nothing until the feature is enabled.

Permissions

Reopen is a dedicated permission; editStatus and editAll do not include it. A role with editAll but without reopen cannot reopen.

Permission key Effect
tickets.reopen · incidents.reopen · problems.reopenMay reopen the respective entity (with reason, within window/limit)
tickets.reopenOverride · incidents.reopenOverride · problems.reopenOverrideBypasses window + limit (NOT the reason requirement, NOT approval). Admin only by default.

The built-in default roles are preset as follows: Agent holds reopen, Admin holds reopen and reopenOverride, end user and approver hold neither. A newly created role must be granted both permissions explicitly. Permissions & RBAC

Configuration (Admin)

Two areas drive the governance — Admin Center → Service Configuration → Reopen & Lifecycle (/admin/lifecycle), permission settings.editGeneral:

  • Reopen Reasons — CRUD per entity (multilingual label, per-reason note requirement, ordering, soft-delete → 204). Errors: duplicate code 409 REOPEN_REASON_CODE_EXISTS; last active reason of an entity 409 REOPEN_REASON_LAST_ACTIVE; unknown id 404 REOPEN_REASON_NOT_FOUND. Reading inactive reasons (?includeInactive=true) requires settings.editGeneral; revoking this permission takes effect immediately. GET/POST/PATCH/DELETE /api/reopen-reasons
  • Lifecycle-Config — window (days + mode CALENDAR/BUSINESS), maximum count, per-role reason requirement, notifyOnReopen, SLA behavior on reopen (slaOnReopenFromResolved/Closed), auto-close, WC-auto-resolve, stale-reminder tiers, escalation threshold. The settings UI shows only the fields relevant per entity per tab (e.g. auto-close/WC-auto-resolve only for tickets). GET /api/lifecycle-config · PUT /api/lifecycle-config/:entityType

Remaining reopen window: GET /api/lifecycle-config/:entityType/reopen-window returns reopenEnabled/Days/From and only requires a login. The detail sidebar shows the remaining time. The full config requires settings.editGeneral.

Auto-Close

Auto-close closes due tickets on a schedule like a normal status change (closedAt, SLA and activity are set). Auto-close exists for tickets only (autoCloseEnabled, off by default). Incidents and problems are always closed manually, because an approval or a PIR determines the closure there.

AspectDetail
Joblifecycle_auto_close (daily ~02:30)
Due timeGREATEST(resolvedAt, latest non-internal message, config.updatedAt)
GraceThe deadline counts from the config save at the earliest (config.updatedAt), so that first activation does not close the entire backlog at once.
Advance warningautoCloseWarnDays → notification TICKET_AUTO_CLOSE_WARNING (customer primary); a customer reply bumps the due time → re-warn

WC-Auto-Resolve (waiting customer)

A step before auto-close: tickets in WAITING_CUSTOMER status the customer does not respond to are automatically set to RESOLVED after a configured deadline (the normal auto-close path may then take over). TICKET only, opt-in.

AspectDetail
Joblifecycle_wc_auto_resolve (daily ~02:00)
Due timelatest non-internal (public) message — customer OR agent (fallback updatedAt) + autoResolveWaitingDays (default 14)
Advance warningautoResolveWaitingWarnDays (default 3) → TICKET_WC_RESOLVE_WARNING (customer primary)
Resolution-CodeautoResolveWaitingCode (default AUTO_RESOLVED_NO_RESPONSEnotifyCustomer=false, EXCLUDE_FROM_REPORTING)
Resetcustomer reply → status WAITING_SUPPORT + wcResolveWarnedAt=null (timer restarts)

Stale Reminder (inactivity)

Inactivity reminder for TICKET, PROBLEM and INCIDENT. Reports records untouched for too long in tiers to assignee (tier 1) → group lead (tier 2) → manager (tier 3), only to people who can see the record. Never changes status or SLA.

  • Job: lifecycle_stale_entity_reminder (daily ~01:30)
  • Config: staleReminderEnabled, staleReminderDaysTier1/2/3 (per entity)
  • Notification: TICKET_STALE_REMINDER / PROBLEM_STALE_REMINDER / INCIDENT_STALE_REMINDER
  • Exclusions: terminal + per type (ticket: ON_HOLD/WAITING_CUSTOMER/SLA-paused/open incident link; problem: ON_HOLD/WAITING_VENDOR; incident: ON_HOLD/PENDING_CLOSURE)

Reopen Escalation

When an entity is reopened more often than the configured threshold (reopenCount ≥ reopenEscalationThreshold), the responsible people are alerted proactively — a quality signal. Recipients are the assignee plus group lead/manager, each only if they may see the record (as with SLA and stale reminders). If nobody eligible is found, the escalation walks up the management chain; if nobody is found there either, the audit entry REOPEN_ESCALATION_NO_RECIPIENT is written.

  • Job: lifecycle_reopen_escalation (daily ~03:00)
  • Threshold: reopenEscalationThreshold (null = off; opt-in per entity)
  • Dedup: reopenEscalatedCount — re-escalation only on each further reopen above the threshold
  • Notification: REOPEN_ESCALATION (internal, not customer-facing)

Notifications

TypRecipientsNote
TICKET_REOPENEDagent / group / customer / followerscustomer gets a generic variant WITHOUT the internal reason/note (redaction)
INCIDENT_REOPENEDassignee / group / reporterinternal
PROBLEM_REOPENEDagent / group / reporterinternal
TICKET_AUTO_CLOSE_WARNINGcustomer primaryadvance warning before auto-close
REOPEN_ESCALATIONassignee + lead/manager (only with visibility of the record)internal, quality signal
TICKET_WC_RESOLVE_WARNINGcustomer primaryadvance warning before WC-auto-resolve
TICKET_STALE_REMINDER · PROBLEM_STALE_REMINDER · INCIDENT_STALE_REMINDERassignee → lead → manager (only with visibility of the record)internal, inactivity escalation

No duplicate notification: A reopen is also a status change. Recipients receive only the *_REOPENED notification, not the generic status-change notification on top. Linked records are informed via the cascading system (LINKED_*_REOPENED). Cascading System

Fields & Analytics

Each entity carries closedAt, reopenCount, reopenedAt and lastReopenReasonCode. Every reopen creates a visible REOPENED timeline entry (reason + note) and an audit event. This feeds the reopen analytics:

EndpointPermissionPurpose
GET /api/analytics/reopenreports.viewAll | viewTeamKPI across all 3 types: everClosed/reopened/reopenRate/cumulativeReopens/top reasons
GET /api/analytics/reopen/detailreports.viewAll | viewTeamtime frame + breakdown per agent/group/category + monthly trend

In the UI: the lifecycle dashboard at /lifecycle-analytics (reporting navigation) with entity tabs, time frame, summary cards, trend and ranked breakdowns; plus a compact reopen-rate widget on the main dashboard.

Related Documentation