The NestJS backend for the Snooze Tickets system. Handles timer persistence, scheduled job execution via Bull Queue, JWT authentication, and automated Zendesk ticket updates (status changes, custom field tracking, tagging).
Works together with the Snooze Zendesk App — the frontend sidebar calls this API to start, stop, and delete timers.
Built for Growon.
- Agent starts a snooze from the Zendesk sidebar →
POST /snooze-timer/startis called - Backend saves the timer to PostgreSQL and schedules a Bull Queue job with a delay equal to the snooze duration
- Zendesk ticket status is immediately updated to the custom "Snoozed" status; a
Total Snoozed Timecustom field is updated with the duration in seconds - When the Bull job fires on expiry → ticket status is set back to
open, asnooze_timer_finishedtag is added, and the snooze field is decremented - If the agent reloads the sidebar,
GET /snooze-timer/:ticketIdreturns the remaining time so the countdown resumes from the correct position - On server restart, a job recovery service re-queues any timers that were running but lost their Bull jobs
| Layer | Technology |
|---|---|
| Framework | NestJS (TypeScript) |
| Database | PostgreSQL + TypeORM |
| Job Queue | Bull Queue + Redis |
| Auth | JWT |
| Zendesk Integration | Zendesk REST API (axios) |
All endpoints except /auth/token are protected by JWT guard.
| Method | Endpoint | Description |
|---|---|---|
| GET | /auth/token?domain= |
Issue a JWT token for a Zendesk subdomain |
| POST | /snooze-timer/start |
Start a snooze timer, schedule Bull job, update Zendesk |
| POST | /snooze-timer/stop |
Pause timer, remove Bull job, save remaining time |
| POST | /snooze-timer/finish |
Mark timer finished, reopen ticket, update snooze field |
| DELETE | /snooze-timer/delete |
Delete timer and Bull job |
| GET | /snooze-timer/:ticketId |
Get current timer state (remaining time, status) |
Bull Queue scheduling — on timer start, a delayed Bull job is added to snooze-queue with a delay in milliseconds equal to the snooze duration. When the job fires, the processor marks the timer as finished and updates Zendesk.
Snooze field tracking — a custom Zendesk ticket field (Total Snoozed Time (seconds)) accumulates the total time a ticket has been snoozed. On start it increments, on stop/delete/finish it decrements by the remaining time. The field is created automatically on first use and added to all ticket forms.
Job recovery on restart — a SnoozeJobRecoveryService runs on startup to detect timers still marked as RUNNING in the database but with no active Bull job (e.g. after a server crash), and re-queues them with the correct remaining delay.
JWT auth — the Zendesk sidebar requests a token via GET /auth/token?domain= and includes it as a Bearer token on all subsequent requests. The JwtGuard validates it on every protected endpoint.
src/
├── app.module.ts # Root module: TypeORM, Bull, Auth, SnoozeTimer
├── auth/ # JWT token generation
├── guards/ # JwtGuard for protected routes
├── snooze_timer/
│ ├── snooze_timer.controller.ts # REST endpoints
│ ├── snooze_timer.service.ts # Core timer logic + Zendesk API calls
│ ├── snooze_timer.processor.ts # Bull job processor (fires on expiry)
│ ├── snooze_timer.entity.ts # TicketTimer entity
│ ├── zendesk_config.entity.ts # Stores snooze field ID per domain
│ └── SnoozeJobRecovery.service.ts # Re-queues lost jobs on startup
└── timer_jobs/ # Timer job tracking table
npm installCreate a .env file:
DB_HOST=
DB_PORT=
DB_USERNAME=
DB_PASSWORD=
DB_DATABASE=
REDIS_HOST=
REDIS_PORT=
ZENDESK_SUBDOMAIN=
ZENDESK_EMAIL=
ZENDESK_API_TOKEN=
ZENDESK_SNOOZED_STATUS_ID=
# development
npm run start:dev
# production
npm run start:prod- Frontend: Snooze-Zendesk-APP — Zendesk sidebar app
Private — built for a client (Growon).