- Added migration 025 for the Ticket System, creating tables for tickets, comments, attachments, worklogs, prepaid cards, and audit logs. - Introduced migration 026 to add ticket-related permissions to the auth system and assign them to user groups. - Developed a test suite for the Ticket Module, validating database schema, ticket number generation, prepaid card constraints, service logic, worklog creation, audit logging, and views.
424 lines
10 KiB
Markdown
424 lines
10 KiB
Markdown
# Ticket System - Email Integration
|
|
|
|
## Overview
|
|
|
|
The ticket system integrates with BMC Hub's email workflow engine to automatically create tickets from incoming emails. Uses **Option B: Message-ID Threading** for robust email-to-ticket linkage.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Incoming Email → Email Classifier → Email Workflow Engine → Ticket Integration
|
|
↓
|
|
create_ticket action
|
|
↓
|
|
EmailTicketIntegration.process_email_for_ticket()
|
|
↓
|
|
┌───────┴───────┐
|
|
↓ ↓
|
|
New Ticket Reply to Existing
|
|
(creates TKT-XXX) (links via Message-ID)
|
|
```
|
|
|
|
## Email Threading Logic
|
|
|
|
### Message-ID Based Threading (Option B)
|
|
|
|
The system uses email headers to detect if an email is part of an existing ticket thread:
|
|
|
|
1. **In-Reply-To Header**: Check if contains `TKT-YYYYMMDD-XXX` pattern
|
|
2. **References Header**: Check all message IDs in chain for ticket number
|
|
3. **Subject Line**: Check for `[TKT-YYYYMMDD-XXX]` or `Re: TKT-YYYYMMDD-XXX`
|
|
|
|
If ticket number found → **Link to existing ticket**
|
|
If NOT found → **Create new ticket**
|
|
|
|
### Ticket Number Format
|
|
|
|
Pattern: `TKT-YYYYMMDD-XXX`
|
|
- `TKT`: Prefix
|
|
- `YYYYMMDD`: Date (e.g., 20251215)
|
|
- `XXX`: Sequential number (001-999)
|
|
|
|
Example: `TKT-20251215-001`
|
|
|
|
## Workflow Actions
|
|
|
|
### 1. `create_ticket`
|
|
|
|
Creates new ticket OR links to existing ticket (smart routing).
|
|
|
|
**Workflow Definition:**
|
|
```json
|
|
{
|
|
"name": "Support Request → Ticket",
|
|
"classification_trigger": "support_request",
|
|
"workflow_steps": [
|
|
{
|
|
"action": "create_ticket",
|
|
"params": {
|
|
"customer_id": 123,
|
|
"assigned_to_user_id": 5
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Parameters:**
|
|
- `customer_id` (optional): BMC customer ID to link ticket to
|
|
- `assigned_to_user_id` (optional): User ID to assign ticket to
|
|
- `priority` (optional): Override priority detection (low/normal/high/critical)
|
|
|
|
**Returns:**
|
|
```json
|
|
{
|
|
"ticket_id": 42,
|
|
"ticket_number": "TKT-20251215-001",
|
|
"created": true,
|
|
"linked": false
|
|
}
|
|
```
|
|
|
|
### 2. `link_email_to_ticket`
|
|
|
|
Explicitly link email to a specific ticket (manual routing).
|
|
|
|
**Workflow Definition:**
|
|
```json
|
|
{
|
|
"action": "link_email_to_ticket",
|
|
"params": {
|
|
"ticket_number": "TKT-20251215-001"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Parameters:**
|
|
- `ticket_number` (required): Target ticket number
|
|
|
|
**Returns:**
|
|
```json
|
|
{
|
|
"ticket_id": 42,
|
|
"ticket_number": "TKT-20251215-001",
|
|
"linked": true
|
|
}
|
|
```
|
|
|
|
## Automatic Features
|
|
|
|
### Tag Extraction
|
|
|
|
Extracts `#hashtags` from email body and adds as ticket tags:
|
|
|
|
```
|
|
Email body: "We need help with #billing #urgent issue"
|
|
→ Ticket tags: ["billing", "urgent"]
|
|
```
|
|
|
|
- Max 10 tags per ticket
|
|
- Minimum 3 characters
|
|
- Converted to lowercase
|
|
|
|
### Priority Detection
|
|
|
|
Automatically determines ticket priority from email content:
|
|
|
|
**Critical**: kritisk, critical, down, nede, urgent, akut
|
|
**High**: høj, high, vigtig, important, haster
|
|
**Low**: lav, low, spørgsmål, question, info
|
|
**Normal**: Everything else (default)
|
|
|
|
Checks both subject line and extracted tags.
|
|
|
|
### Email Metadata
|
|
|
|
Stores rich metadata in ticket:
|
|
|
|
- **Description**: Formatted with sender email, received date, original body
|
|
- **Custom Fields**: `email_from`, `email_message_id`, `created_from_email`
|
|
- **Email Log**: Full threading data in `tticket_email_log` table
|
|
|
|
## Database Storage
|
|
|
|
### `tticket_email_log` Table
|
|
|
|
Stores all email-ticket linkages for audit trail:
|
|
|
|
```sql
|
|
CREATE TABLE tticket_email_log (
|
|
id SERIAL PRIMARY KEY,
|
|
ticket_id INTEGER NOT NULL REFERENCES tticket_tickets(id),
|
|
email_message_id TEXT NOT NULL,
|
|
email_subject TEXT,
|
|
email_from TEXT NOT NULL,
|
|
email_received_at TIMESTAMP,
|
|
is_reply BOOLEAN DEFAULT FALSE,
|
|
thread_data JSONB,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
```
|
|
|
|
**`thread_data` JSONB structure:**
|
|
```json
|
|
{
|
|
"message_id": "<abc123@example.com>",
|
|
"in_reply_to": "<TKT-20251215-001@bmcnetworks.dk>",
|
|
"references": "<ref1@example.com> <ref2@example.com>"
|
|
}
|
|
```
|
|
|
|
## Usage Examples
|
|
|
|
### Example 1: Basic Support Request
|
|
|
|
**Incoming Email:**
|
|
```
|
|
From: customer@example.com
|
|
Subject: Help with network issue
|
|
Body: Our internet is down. Can you help?
|
|
```
|
|
|
|
**Workflow Configuration:**
|
|
```json
|
|
{
|
|
"name": "Support Email → Ticket",
|
|
"classification_trigger": "support_request",
|
|
"confidence_threshold": 0.7,
|
|
"workflow_steps": [
|
|
{
|
|
"action": "create_ticket",
|
|
"params": {
|
|
"assigned_to_user_id": 5
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Result:**
|
|
- Creates ticket `TKT-20251215-001`
|
|
- Priority: `normal` (no urgent keywords)
|
|
- Tags: [] (no hashtags found)
|
|
- Assigned to user 5
|
|
- Email logged in `tticket_email_log`
|
|
|
|
### Example 2: Email Reply to Ticket
|
|
|
|
**Incoming Email:**
|
|
```
|
|
From: customer@example.com
|
|
Subject: Re: TKT-20251215-001
|
|
In-Reply-To: <TKT-20251215-001@bmcnetworks.dk>
|
|
Body: Thanks, issue is resolved now
|
|
```
|
|
|
|
**Result:**
|
|
- **Detects existing ticket** via In-Reply-To header
|
|
- **Adds comment** to ticket `TKT-20251215-001`
|
|
- Does NOT create new ticket
|
|
- Logs as reply (`is_reply=true`)
|
|
|
|
### Example 3: Tagged Urgent Request
|
|
|
|
**Incoming Email:**
|
|
```
|
|
From: vip@example.com
|
|
Subject: URGENT: Server down!
|
|
Body: Production server is down #critical #server
|
|
```
|
|
|
|
**Result:**
|
|
- Creates ticket with priority `critical` (subject keyword)
|
|
- Tags: `["critical", "server"]`
|
|
- Custom field: `created_from_email=true`
|
|
|
|
## API Endpoints
|
|
|
|
### Get Ticket Email Thread
|
|
|
|
```http
|
|
GET /api/v1/tickets/{ticket_id}/emails
|
|
```
|
|
|
|
Returns chronological list of all emails linked to ticket.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"ticket_id": 42,
|
|
"ticket_number": "TKT-20251215-001",
|
|
"emails": [
|
|
{
|
|
"id": 1,
|
|
"email_message_id": "<abc123@example.com>",
|
|
"email_from": "customer@example.com",
|
|
"email_subject": "Help with issue",
|
|
"email_received_at": "2025-12-15T10:00:00Z",
|
|
"is_reply": false,
|
|
"created_at": "2025-12-15T10:01:00Z"
|
|
},
|
|
{
|
|
"id": 2,
|
|
"email_message_id": "<def456@example.com>",
|
|
"email_from": "customer@example.com",
|
|
"email_subject": "Re: TKT-20251215-001",
|
|
"email_received_at": "2025-12-15T11:00:00Z",
|
|
"is_reply": true,
|
|
"created_at": "2025-12-15T11:01:00Z"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Find Tickets by Email Address
|
|
|
|
```http
|
|
GET /api/v1/tickets/by-email/{email_address}
|
|
```
|
|
|
|
Returns all tickets associated with an email address.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"email_address": "customer@example.com",
|
|
"tickets": [
|
|
{
|
|
"id": 42,
|
|
"ticket_number": "TKT-20251215-001",
|
|
"subject": "Network issue",
|
|
"status": "open",
|
|
"created_at": "2025-12-15T10:00:00Z"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Settings (`.env`)
|
|
|
|
```bash
|
|
# Ticket system enabled
|
|
TICKET_ENABLED=true
|
|
|
|
# Email integration enabled (requires email system)
|
|
TICKET_EMAIL_INTEGRATION=true
|
|
|
|
# Auto-assign new tickets (requires user_id)
|
|
TICKET_AUTO_ASSIGN=false
|
|
TICKET_DEFAULT_ASSIGNEE_ID=5
|
|
|
|
# Default priority for new tickets
|
|
TICKET_DEFAULT_PRIORITY=normal
|
|
```
|
|
|
|
## Testing
|
|
|
|
### Test Email-to-Ticket Creation
|
|
|
|
```bash
|
|
curl -X POST http://localhost:8001/api/v1/test/email-to-ticket \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"email_data": {
|
|
"message_id": "<test123@example.com>",
|
|
"subject": "Test ticket",
|
|
"from_address": "test@example.com",
|
|
"body": "This is a test #testing",
|
|
"received_at": "2025-12-15T10:00:00Z"
|
|
}
|
|
}'
|
|
```
|
|
|
|
### Test Email Reply Linking
|
|
|
|
```bash
|
|
curl -X POST http://localhost:8001/api/v1/test/email-to-ticket \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"email_data": {
|
|
"message_id": "<reply456@example.com>",
|
|
"subject": "Re: TKT-20251215-001",
|
|
"from_address": "test@example.com",
|
|
"body": "Reply to existing ticket",
|
|
"received_at": "2025-12-15T11:00:00Z",
|
|
"in_reply_to": "<TKT-20251215-001@bmcnetworks.dk>"
|
|
}
|
|
}'
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Tickets Not Created
|
|
|
|
**Check:**
|
|
1. Email workflow engine enabled (`EMAIL_WORKFLOWS_ENABLED=true`)
|
|
2. Workflow exists for classification trigger
|
|
3. Confidence threshold met
|
|
4. Workflow action is `create_ticket` (NOT old `create_ticket` stub)
|
|
|
|
**Debug:**
|
|
```sql
|
|
-- Check workflow executions
|
|
SELECT * FROM email_workflow_executions
|
|
WHERE status = 'failed'
|
|
ORDER BY created_at DESC
|
|
LIMIT 10;
|
|
```
|
|
|
|
### Email Not Linked to Ticket
|
|
|
|
**Check:**
|
|
1. Ticket number format correct (`TKT-YYYYMMDD-XXX`)
|
|
2. Ticket exists in database
|
|
3. Email headers contain ticket number (In-Reply-To, References, Subject)
|
|
|
|
**Debug:**
|
|
```sql
|
|
-- Check email logs
|
|
SELECT * FROM tticket_email_log
|
|
WHERE ticket_id = 42
|
|
ORDER BY created_at DESC;
|
|
```
|
|
|
|
### Duplicate Tickets Created
|
|
|
|
**Check:**
|
|
1. Email reply headers missing ticket number
|
|
2. Subject line doesn't match pattern (e.g., `Re: Ticket 123` instead of `Re: TKT-20251215-001`)
|
|
|
|
**Solution:**
|
|
- Ensure outgoing ticket emails include ticket number in subject: `[TKT-20251215-001]`
|
|
- Add ticket number to Message-ID: `<TKT-20251215-001-reply@bmcnetworks.dk>`
|
|
|
|
## Best Practices
|
|
|
|
1. **Include Ticket Number in Replies**: Always include `[TKT-YYYYMMDD-XXX]` in subject line
|
|
2. **Use Message-ID with Ticket Number**: Format: `<TKT-YYYYMMDD-XXX@bmcnetworks.dk>`
|
|
3. **Set Customer ID in Workflow**: Improves ticket organization and reporting
|
|
4. **Monitor Workflow Executions**: Check `email_workflow_executions` table regularly
|
|
5. **Review Failed Actions**: Alert on repeated workflow failures
|
|
|
|
## Security Considerations
|
|
|
|
1. **No Email Body Storage**: Only stores metadata and body in ticket description
|
|
2. **Sender Validation**: Consider implementing sender verification (SPF/DKIM)
|
|
3. **Spam Prevention**: Email classifier should filter spam before workflow execution
|
|
4. **Customer Isolation**: Ensure `customer_id` properly set to prevent data leakage
|
|
|
|
## Future Enhancements
|
|
|
|
- **Attachment Handling**: Link email attachments to ticket attachments
|
|
- **Email Templates**: Auto-reply with ticket number
|
|
- **SLA Integration**: Start SLA timer on ticket creation from email
|
|
- **Multi-Ticket Threading**: Support one email creating multiple tickets
|
|
- **Smart Customer Detection**: Auto-detect customer from sender domain
|
|
|
|
## Related Documentation
|
|
|
|
- [Ticket System Architecture](TICKET_SYSTEM_ARCHITECTURE.md)
|
|
- [Email Workflow System](EMAIL_WORKFLOWS.md)
|
|
- [Database Schema](../migrations/025_ticket_module.sql)
|