Version: Arctyk ITSM v0.6.0+ Last Updated: January 2026
Email Integration¶
Arctyk ITSM sends email notifications for ticket events and supports SMTP configuration for production deployments.
Overview¶
Email Features¶
- ✅ Ticket creation notifications
- ✅ Ticket assignment notifications
- ✅ Status change notifications
- ✅ Comment notifications
- ✅ Overdue ticket alerts
- 🔄 Email-triggered ticket creation (planned)
Current Status¶
- Version: v0.6.0+
- Backend: Django email framework
- Queue: Celery async task processing
- Default: Console backend (prints to stdout in development)
Configuration¶
Development (Console Backend)¶
By default, Arctyk ITSM prints emails to console output:
Emails appear in your Django console:
[29/Nov/2025 10:00:00] "POST /tickets/" - New ticket created
...
Subject: New Ticket: Unable to login
From: noreply@arctyk.example.com
To: requester@example.com
Email body content...
Production (SMTP Backend)¶
Configure SMTP for sending real emails:
# src/config/settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'your-email@gmail.com'
EMAIL_HOST_PASSWORD = 'your-app-password'
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = 'arctyk@your-domain.com'
Environment Variables¶
Set via .env:
# SMTP Configuration
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_HOST_USER=your-email@gmail.com
EMAIL_HOST_PASSWORD=your-app-password
EMAIL_USE_TLS=True
DEFAULT_FROM_EMAIL=arctyk@your-domain.com
# Alternative: SendGrid
EMAIL_BACKEND=sendgrid_backend.SendgridBackend
SENDGRID_API_KEY=your-sendgrid-api-key
Email Events¶
Ticket Created¶
Sent to:
- Ticket requester
- Ticket assigned user
- Project team (if configured)
Example Email:
Subject: New Ticket: Unable to login (TKT-001)
From: noreply@arctyk.example.com
To: user@example.com
Hello John Doe,
A new ticket has been created:
Title: Unable to login
Ticket Number: TKT-001
Priority: High
Description: Users cannot access the system
View Ticket: https://arctyk.example.com/tickets/1/
---
Arctyk ITSM
Ticket Assigned¶
Sent to newly assigned user.
Subject: Ticket Assigned: {title} (TKT-{number})
Status Changed¶
Sent to:
- Requester (if public status)
- Assigned user
- Project team
Subject: Ticket Status Changed: {title} → {new_status}
Comment Added¶
Sent to:
- Ticket requester
- Ticket assigned user
- Other commenters (if public comment)
Subject: New Comment on Ticket: {title} (TKT-{number})
Note: Internal comments are not emailed.
Overdue Alert¶
Sent to assigned user for tickets past due date.
Subject: Overdue Ticket: {title} (TKT-{number})
Email Implementation¶
Celery Task¶
Emails are sent asynchronously via Celery:
# src/tickets/tasks.py
from celery import shared_task
from django.core.mail import send_mail
@shared_task
def send_ticket_notification(ticket_id, event_type):
"""Send email notification for ticket event."""
ticket = Ticket.objects.get(pk=ticket_id)
if event_type == 'created':
recipients = [ticket.requester.email]
subject = f'New Ticket: {ticket.title} ({ticket.ticket_number})'
message = render_email_template('ticket_created.html', {'ticket': ticket})
elif event_type == 'assigned':
recipients = [ticket.assigned_user.email]
subject = f'Ticket Assigned: {ticket.title}'
message = render_email_template('ticket_assigned.html', {'ticket': ticket})
send_mail(
subject=subject,
message=message,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=recipients,
html_message=message,
)
Signal-Based Triggers¶
Emails are triggered by Django signals:
# src/tickets/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .tasks import send_ticket_notification
@receiver(post_save, sender=Ticket)
def ticket_created(sender, instance, created, **kwargs):
"""Send email when ticket is created."""
if created:
send_ticket_notification.delay(instance.id, 'created')
@receiver(post_save, sender=Ticket)
def ticket_status_changed(sender, instance, **kwargs):
"""Send email when ticket status changes."""
old = Ticket.objects.get(pk=instance.pk)
if old.status != instance.status:
send_ticket_notification.delay(instance.id, 'status_changed')
Email Templates¶
Email templates are HTML files in templates/emails/:
templates/emails/
├── base.html # Base email template
├── ticket_created.html
├── ticket_assigned.html
├── status_changed.html
├── comment_added.html
└── overdue_alert.html
Template Example¶
{# templates/emails/ticket_created.html #}
{% extends "emails/base.html" %}
{% block content %}
<h2>New Ticket Created</h2>
<p>Hello {{ ticket.requester.first_name }},</p>
<p>A new ticket has been created in Arctyk ITSM:</p>
<table>
<tr>
<td><strong>Title:</strong></td>
<td>{{ ticket.title }}</td>
</tr>
<tr>
<td><strong>Ticket Number:</strong></td>
<td>{{ ticket.ticket_number }}</td>
</tr>
<tr>
<td><strong>Priority:</strong></td>
<td>{{ ticket.get_priority_display }}</td>
</tr>
</table>
<p>
<a href="{{ site_url }}/tickets/{{ ticket.id }}/">View Ticket</a>
</p>
{% endblock %}
Testing Email¶
Console Backend¶
Emails print to console in development:
When a ticket is created, emails appear in console output.
Send Test Email¶
Use Django shell:
python manage.py shell
>>> from django.core.mail import send_mail
>>> send_mail(
... 'Test Email',
... 'This is a test',
... 'from@example.com',
... ['to@example.com'],
... )
1
Email Logging¶
Monitor email sending:
Troubleshooting¶
Emails Not Sending¶
Check Configuration:
python manage.py shell
>>> from django.conf import settings
>>> settings.EMAIL_BACKEND
'django.core.mail.backends.console.EmailBackend'
>>> settings.EMAIL_HOST
'smtp.gmail.com'
Check Celery:
# Verify Celery worker is running
celery -A config worker --loglevel=info
# Monitor task queue
python manage.py shell
>>> from django.core.mail.outbox import outbox
>>> len(outbox) # Should see failed tasks
SMTP Connection Error¶
Solutions:
- Verify credentials are correct
- For Gmail: Use "App Password", not your regular password
- Check firewall allows outbound SMTP connections
- Verify
EMAIL_USE_TLS=Truefor port 587
Rate Limiting¶
Email providers often rate-limit sending:
- Gmail: 500 emails per 24 hours
- SendGrid: Based on plan
- AWS SES: Sandbox mode: 1 email/second
Solution: Space out emails or use a transactional email service.
Best Practices¶
1. Use Task Queue¶
Always send emails async via Celery:
2. Handle Bounces¶
Track email bounces to update user email addresses:
def handle_bounce(email):
"""Mark email as bounced."""
user = User.objects.get(email=email)
user.email_bounced = True
user.save()
3. Unsubscribe Option¶
Always include unsubscribe link:
4. Use Reply-To¶
Set reply-to address for user responses:
send_mail(
subject='...',
message='...',
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[...],
reply_to=['support@example.com'], # Allow user replies
)
5. Monitor Deliverability¶
Use email service provider dashboard to monitor:
- Delivery rates
- Open rates
- Click rates
- Bounce rates
Future Enhancements¶
Planned Features (v0.7.0+)¶
- Email-triggered ticket creation
- Inline reply handling
- Email signature preservation
- Attachment handling
- User email preferences
- Email digest/summary
- Multi-language email templates
Integration Examples¶
# Parse incoming email and create ticket
def handle_incoming_email(email_message):
"""Create ticket from incoming email."""
ticket = Ticket.objects.create(
title=email_message.subject,
description=email_message.body,
requester=email_message.sender,
)
return ticket