Skip to main content

Embeddable support ticket system for Django

Project description

Escalated for Django

A full-featured, embeddable support ticket system for Django. Drop it into any app — get a complete helpdesk with SLA tracking, escalation rules, agent workflows, and a customer portal. No external services required.

Three hosting modes. Run entirely self-hosted, sync to a central cloud for multi-app visibility, or proxy everything to the cloud. Switch modes with a single config change.

Features

  • Ticket lifecycle — Create, assign, reply, resolve, close, reopen with configurable status transitions
  • SLA engine — Per-priority response and resolution targets, business hours calculation, automatic breach detection
  • Escalation rules — Condition-based rules that auto-escalate, reprioritize, reassign, or notify
  • Agent dashboard — Ticket queue with filters, bulk actions, internal notes, canned responses
  • Customer portal — Self-service ticket creation, replies, and status tracking
  • Admin panel — Manage departments, SLA policies, escalation rules, tags, and view reports
  • File attachments — Drag-and-drop uploads with configurable storage and size limits
  • Activity timeline — Full audit log of every action on every ticket
  • Email notifications — Configurable per-event notifications with webhook support
  • Department routing — Organize agents into departments with auto-assignment (round-robin)
  • Tagging system — Categorize tickets with colored tags
  • Inertia.js + Vue 3 UI — Shared frontend via @escalated-dev/escalated

v0.4.0 — Advanced Features

  • Bulk actions — Assign, change status/priority, add tags, close, or delete multiple tickets at once
  • Macros — Reusable multi-step automations (set status + assign + add note in one click)
  • Ticket followers — Agents follow tickets and receive the same notifications as the assignee
  • Satisfaction ratings — 1-5 star CSAT ratings with optional comments after resolution
  • Pinned notes — Pin important internal notes to the top of the ticket thread
  • Keyboard shortcuts — Full keyboard navigation for power users
  • Quick filters — One-click filter chips (My Tickets, Unassigned, Urgent, SLA Breaching)
  • Presence indicators — See who else is viewing a ticket in real-time
  • Enhanced dashboard — CSAT metrics, resolution times, SLA breach tracking

Requirements

  • Python 3.10+
  • Django 4.2+
  • Node.js 18+ (for frontend assets)

Quick Start

pip install escalated-django
npm install @escalated-dev/escalated

1. Add to INSTALLED_APPS

INSTALLED_APPS = [
    # ...
    'django.contrib.contenttypes',
    'inertia',
    'escalated',
]

2. Include URLs

from django.urls import path, include

urlpatterns = [
    # ...
    path("support/", include("escalated.urls")),
]

3. Run migrations

python manage.py migrate escalated

Visit /support — you're live.

Frontend Setup

Escalated uses Inertia.js with Vue 3. The frontend components are provided by the @escalated-dev/escalated npm package.

Tailwind Content

Add the Escalated package to your Tailwind content config so its classes aren't purged:

// tailwind.config.js
content: [
    // ... your existing paths
    './node_modules/@escalated-dev/escalated/src/**/*.vue',
],

Page Resolver

Add the Escalated pages to your Inertia page resolver:

// frontend/main.js
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'

createInertiaApp({
  resolve: name => {
    if (name.startsWith('Escalated/')) {
      const escalatedPages = import.meta.glob(
        '../node_modules/@escalated-dev/escalated/src/pages/**/*.vue',
        { eager: true }
      )
      const pageName = name.replace('Escalated/', '')
      return escalatedPages[`../node_modules/@escalated-dev/escalated/src/pages/${pageName}.vue`]
    }

    const pages = import.meta.glob('./pages/**/*.vue', { eager: true })
    return pages[`./pages/${name}.vue`]
  },
  setup({ el, App, props, plugin }) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .mount(el)
  },
})

Theming (Optional)

Register the EscalatedPlugin to render Escalated pages inside your app's layout — no page duplication needed:

import { EscalatedPlugin } from '@escalated-dev/escalated'
import BaseLayout from '@/layouts/BaseLayout.vue'

createInertiaApp({
  setup({ el, App, props, plugin }) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .use(EscalatedPlugin, {
        layout: BaseLayout,
        theme: {
          primary: '#3b82f6',
          radius: '0.75rem',
        }
      })
      .mount(el)
  },
})

Your layout component must accept a #header slot and a default slot. Escalated will render its sub-navigation in the header and page content in the default slot. Without the plugin, Escalated uses its own standalone layout.

See the @escalated-dev/escalated README for full theming documentation and CSS custom properties.

Hosting Modes

Self-Hosted (default)

Everything stays in your database. No external calls. Full autonomy.

ESCALATED = {
    "MODE": "self_hosted",
}

Synced

Local database + automatic sync to cloud.escalated.dev for unified inbox across multiple apps. If the cloud is unreachable, your app keeps working — events queue and retry.

ESCALATED = {
    "MODE": "synced",
    "HOSTED_API_URL": "https://cloud.escalated.dev/api/v1",
    "HOSTED_API_KEY": "your-api-key",
}

Cloud

All ticket data proxied to the cloud API. Your app handles auth and renders UI, but storage lives in the cloud.

ESCALATED = {
    "MODE": "cloud",
    "HOSTED_API_URL": "https://cloud.escalated.dev/api/v1",
    "HOSTED_API_KEY": "your-api-key",
}

All three modes share the same views, UI, and business logic. The driver pattern handles the rest.

Configuration

Add to your settings.py:

ESCALATED = {
    "MODE": "self_hosted",              # self_hosted | synced | cloud
    "TABLE_PREFIX": "escalated_",
    "ROUTE_PREFIX": "support",
    "DEFAULT_PRIORITY": "medium",

    # Tickets
    "ALLOW_CUSTOMER_CLOSE": True,
    "AUTO_CLOSE_RESOLVED_AFTER_DAYS": 7,
    "MAX_ATTACHMENTS": 5,
    "MAX_ATTACHMENT_SIZE_KB": 10240,

    # SLA
    "SLA": {
        "ENABLED": True,
        "BUSINESS_HOURS_ONLY": False,
        "BUSINESS_HOURS": {
            "START": "09:00",
            "END": "17:00",
            "TIMEZONE": "UTC",
            "DAYS": [1, 2, 3, 4, 5],
        },
    },

    # Notifications
    "NOTIFICATION_CHANNELS": ["email"],
    "WEBHOOK_URL": None,

    # Cloud/Synced mode
    "HOSTED_API_URL": "https://cloud.escalated.dev/api/v1",
    "HOSTED_API_KEY": None,
}

Management Commands

# Check SLA deadlines and fire breach notifications
python manage.py check_sla

# Evaluate escalation rules against open tickets
python manage.py evaluate_escalations

# Auto-close tickets resolved more than N days ago
python manage.py close_resolved --days 7

# Purge old activity logs
python manage.py purge_activities --days 90

Schedule these with cron, Celery Beat, or django-crontab for automated enforcement.

Routes

All routes use the configurable prefix (default: support).

Route Method Description
/support/tickets/ GET Customer ticket list
/support/tickets/create/ GET New ticket form
/support/tickets/<id>/ GET Ticket detail
/support/agent/ GET Agent dashboard
/support/agent/tickets/ GET Agent ticket queue
/support/agent/tickets/<id>/ GET Agent ticket view
/support/admin/reports/ GET Admin reports
/support/admin/departments/ GET Department management
/support/admin/sla-policies/ GET SLA policy management
/support/admin/escalation-rules/ GET Escalation rule management
/support/admin/tags/ GET Tag management
/support/admin/canned-responses/ GET Canned response management
/support/agent/tickets/bulk/ POST Bulk actions on multiple tickets
/support/agent/tickets/<id>/follow/ POST Follow/unfollow a ticket
/support/agent/tickets/<id>/macro/ POST Apply a macro to a ticket
/support/agent/tickets/<id>/presence/ POST Update presence on a ticket
/support/agent/tickets/<id>/pin/<reply_id>/ POST Pin/unpin an internal note
/support/tickets/<id>/rate/ POST Submit satisfaction rating

Signals

Connect to ticket lifecycle events:

from escalated.signals import ticket_created, ticket_resolved

@receiver(ticket_created)
def on_ticket_created(sender, ticket, user, **kwargs):
    print(f"New ticket: {ticket.reference}")

@receiver(ticket_resolved)
def on_ticket_resolved(sender, ticket, user, **kwargs):
    print(f"Resolved: {ticket.reference}")

Available signals: ticket_created, ticket_updated, ticket_status_changed, ticket_assigned, ticket_unassigned, ticket_priority_changed, ticket_escalated, ticket_resolved, ticket_closed, ticket_reopened, reply_created, internal_note_added, sla_breached, sla_warning, tag_added, tag_removed, department_changed.

Also Available For

Same architecture, same Vue UI, same three hosting modes — for every major backend framework.

Development

pip install -e ".[dev]"
pytest

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

escalated_django-0.4.0.tar.gz (76.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

escalated_django-0.4.0-py3-none-any.whl (99.7 kB view details)

Uploaded Python 3

File details

Details for the file escalated_django-0.4.0.tar.gz.

File metadata

  • Download URL: escalated_django-0.4.0.tar.gz
  • Upload date:
  • Size: 76.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for escalated_django-0.4.0.tar.gz
Algorithm Hash digest
SHA256 da9aa01c45d6b5a078dfbfaefd5161f154b060ddba63ce8dbdc67483318ec777
MD5 d2f231039ec67befa57697332af8fde3
BLAKE2b-256 260c0aafec71a6ddfba7612bf8ee37a48ebd824b99903452ca850a9082db1ea9

See more details on using hashes here.

File details

Details for the file escalated_django-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for escalated_django-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a0ad0cfeb73b9edb2101ea9a2fd34381c9cb8959777768c91868a40f8da957d3
MD5 a72055fb2c2edfcc7b93ad385f66fbfb
BLAKE2b-256 da045219743e2a1f91c9b713d5878f6cd8a498aac59978eb617d47a923fe67ec

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page