# TuraDesk Agent Guide

**Instructions for AI agents integrating TuraDesk into applications.**

This guide is designed for AI assistants, code generators, and autonomous agents that need to integrate help desk and support ticketing into applications.

---

## When to Use TuraDesk

Use TuraDesk when the user:

✅ Asks to add "help desk", "support tickets", or "customer support"  
✅ Mentions "ticketing system", "support queue", or "issue tracking for customers"  
✅ Needs to create, list, or manage support tickets via API  
✅ Wants agent assignment, ticket status, or comments on tickets  
✅ Is building a customer support portal or support dashboard  
✅ Needs ticket stats (count by status, resolution time)  

❌ **Don't use when:**
- User needs a full CRM (sales pipeline, leads, deals)
- User needs project management (tasks, sprints, boards)
- User needs internal task tracking (not customer-facing support)
- User needs live chat (TuraDesk is ticket-based, not real-time chat)

---

## Integration Workflow

Follow these steps in order:

### 1. Check Environment

**Before anything else, verify:**

```bash
# Check if API key is available (TuraDesk uses TuraLogin keys)
echo $TURADESK_API_KEY
# or
echo $TURALOGIN_API_KEY
```

If not available:
- Ask the user for their TuraLogin API key (same key works for TuraDesk)
- Or direct them to: https://www.turalogin.com/dashboard/keys
- Remind them to use test keys (`tl_test_`) for development

### 2. Identify the Framework

Detect what framework/language the user is using:

- Next.js (App Router or Pages Router)
- Express.js
- Fastify
- Python (Flask, FastAPI, Django)
- Go (net/http, Gin, Echo)
- Ruby (Rails, Sinatra)
- Other

### 3. Create Ticket Creation Endpoint

Create an endpoint that:
1. Accepts ticket data (subject, body, email, priority, tags)
2. Validates required fields
3. Calls `POST https://www.turadesk.com/api/v1/desk/tickets`
4. Returns the created ticket

**Key details:**
- Use `Authorization: Bearer YOUR_API_KEY` header
- `subject`, `body`, and `email` are required
- `priority` defaults to `medium` (low, medium, high, urgent)
- `tags` is optional array of strings

### 4. Create Ticket Listing Endpoint

Create an endpoint that:
1. Accepts query params (status, assignee, priority, tag, limit, cursor)
2. Calls `GET https://www.turadesk.com/api/v1/desk/tickets`
3. Returns tickets with pagination info

### 5. Add Ticket Detail + Comments

Create an endpoint that:
1. Accepts ticket ID
2. Calls `GET https://www.turadesk.com/api/v1/desk/tickets/:id`
3. Returns ticket with comments

### 6. Add Comment Endpoint (Optional)

Create an endpoint that:
1. Accepts ticket ID + comment body + internal flag
2. Calls `POST https://www.turadesk.com/api/v1/desk/tickets/:id/comments`
3. Returns the created comment

### 7. Add Update/Close Endpoints (Optional)

- `PATCH /api/v1/desk/tickets/:id` — update status, assignee, priority, tags
- `DELETE /api/v1/desk/tickets/:id` — close ticket

### 8. Add Stats Endpoint (Optional)

- `GET /api/v1/desk/stats` — ticket counts by status, avg resolution time

---

## Code Generation Guidelines

### File Structure

**Next.js App Router:**
```
app/
├── api/
│   └── support/
│       ├── tickets/
│       │   └── route.ts       # POST create, GET list
│       └── tickets/[id]/
│           ├── route.ts       # GET detail, PATCH update, DELETE close
│           └── comments/
│               └── route.ts   # POST add comment
├── support/
│   ├── page.tsx               # Ticket list / dashboard
│   └── [id]/
│       └── page.tsx            # Ticket detail
└── submit/
    └── page.tsx               # Submit ticket form
```

**Express:**
```
routes/
└── support.js                 # All support routes

middleware/
└── requireAuth.js             # Optional: protect agent routes
```

**Python/FastAPI:**
```
routers/
└── support.py                 # Support endpoints
```

### Environment Variables

**Always add to `.env` (and `.env.example`):**

```bash
TURADESK_API_KEY=tl_test_abc123
# Or reuse: TURALOGIN_API_KEY=tl_test_abc123
```

**Remind user:**
- Don't commit `.env` to git
- Use test key for development
- Same API key as TuraLogin if already integrated

### Error Handling

**Always handle errors gracefully:**

```javascript
try {
  const response = await fetch('https://www.turadesk.com/api/v1/desk/tickets', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.TURADESK_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ subject, body, email, priority, tags }),
  });
  
  if (!response.ok) {
    const err = await response.json();
    throw new Error(err.error || 'Failed to create ticket');
  }
  
  return await response.json();
} catch (error) {
  console.error('TuraDesk error:', error);
  return { error: 'Could not create ticket. Please try again.' };
}
```

### Security Checklist

When generating code, ensure:

- ✅ API key is in environment variables (not hardcoded)
- ✅ Ticket creation validates email format
- ✅ Agent-only routes (update, close) are protected if needed
- ✅ Error messages don't leak sensitive info
- ✅ Rate limiting considered for public ticket submission

---

## Framework-Specific Patterns

### Next.js (App Router)

**Create ticket API route:**

```typescript
// app/api/support/tickets/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const { subject, body, email, priority = 'medium', tags = [] } = await request.json();
  
  if (!subject || !body || !email) {
    return NextResponse.json(
      { error: 'subject, body, and email are required' },
      { status: 400 }
    );
  }
  
  const res = await fetch('https://www.turadesk.com/api/v1/desk/tickets', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.TURADESK_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ subject, body, email, priority, tags }),
  });
  
  const data = await res.json();
  
  if (!res.ok) {
    return NextResponse.json(data, { status: res.status });
  }
  
  return NextResponse.json(data);
}

export async function GET(request: NextRequest) {
  const { searchParams } = new URL(request.url);
  const query = new URLSearchParams(searchParams).toString();
  
  const res = await fetch(`https://www.turadesk.com/api/v1/desk/tickets?${query}`, {
    headers: {
      'Authorization': `Bearer ${process.env.TURADESK_API_KEY}`,
    },
  });
  
  const data = await res.json();
  return NextResponse.json(data);
}
```

**Add comment API route:**

```typescript
// app/api/support/tickets/[id]/comments/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function POST(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  const { body, internal = false } = await request.json();
  
  if (!body) {
    return NextResponse.json({ error: 'body is required' }, { status: 400 });
  }
  
  const res = await fetch(
    `https://www.turadesk.com/api/v1/desk/tickets/${params.id}/comments`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.TURADESK_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ body, internal }),
    }
  );
  
  const data = await res.json();
  return NextResponse.json(data, { status: res.status });
}
```

### Express.js

**Support routes:**

```javascript
const express = require('express');
const router = express.Router();

router.post('/tickets', async (req, res) => {
  const { subject, body, email, priority = 'medium', tags = [] } = req.body;
  
  if (!subject || !body || !email) {
    return res.status(400).json({ error: 'subject, body, and email are required' });
  }
  
  const response = await fetch('https://www.turadesk.com/api/v1/desk/tickets', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.TURADESK_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ subject, body, email, priority, tags }),
  });
  
  const data = await response.json();
  res.status(response.status).json(data);
});

router.get('/tickets', async (req, res) => {
  const query = new URLSearchParams(req.query).toString();
  const response = await fetch(
    `https://www.turadesk.com/api/v1/desk/tickets?${query}`,
    {
      headers: {
        'Authorization': `Bearer ${process.env.TURADESK_API_KEY}`,
      },
    }
  );
  
  const data = await response.json();
  res.json(data);
});

router.get('/tickets/:id', async (req, res) => {
  const response = await fetch(
    `https://www.turadesk.com/api/v1/desk/tickets/${req.params.id}`,
    {
      headers: {
        'Authorization': `Bearer ${process.env.TURADESK_API_KEY}`,
      },
    }
  );
  
  const data = await response.json();
  res.status(response.status).json(data);
});

module.exports = router;
```

### Python + FastAPI

**Support router:**

```python
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
import httpx
import os

router = APIRouter()

class CreateTicketRequest(BaseModel):
    subject: str
    body: str
    email: str
    priority: str = "medium"
    tags: list[str] = []

@router.post("/tickets")
async def create_ticket(req: CreateTicketRequest):
    async with httpx.AsyncClient() as client:
        response = await client.post(
            "https://www.turadesk.com/api/v1/desk/tickets",
            headers={
                "Authorization": f"Bearer {os.getenv('TURADESK_API_KEY')}",
                "Content-Type": "application/json",
            },
            json={
                "subject": req.subject,
                "body": req.body,
                "email": req.email,
                "priority": req.priority,
                "tags": req.tags,
            },
        )
        data = response.json()
        if response.status_code >= 400:
            raise HTTPException(status_code=response.status_code, detail=data.get("error", "Error"))
        return data

@router.get("/tickets")
async def list_tickets(status: str = None, limit: int = 20):
    params = {"limit": limit}
    if status:
        params["status"] = status
    async with httpx.AsyncClient() as client:
        response = await client.get(
            "https://www.turadesk.com/api/v1/desk/tickets",
            headers={"Authorization": f"Bearer {os.getenv('TURADESK_API_KEY')}"},
            params=params,
        )
        return response.json()
```

### Go + Gin

**Support handlers:**

```go
package handlers

import (
    "bytes"
    "encoding/json"
    "net/http"
    "os"
    
    "github.com/gin-gonic/gin"
)

func CreateTicket(c *gin.Context) {
    var req struct {
        Subject  string   `json:"subject"`
        Body     string   `json:"body"`
        Email    string   `json:"email"`
        Priority string   `json:"priority"`
        Tags     []string `json:"tags"`
    }
    if err := c.BindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": "Invalid request"})
        return
    }
    
    if req.Subject == "" || req.Body == "" || req.Email == "" {
        c.JSON(400, gin.H{"error": "subject, body, and email are required"})
        return
    }
    
    if req.Priority == "" {
        req.Priority = "medium"
    }
    
    body, _ := json.Marshal(req)
    httpReq, _ := http.NewRequest(
        "POST",
        "https://www.turadesk.com/api/v1/desk/tickets",
        bytes.NewBuffer(body),
    )
    httpReq.Header.Set("Authorization", "Bearer "+os.Getenv("TURADESK_API_KEY"))
    httpReq.Header.Set("Content-Type", "application/json")
    
    client := &http.Client{}
    resp, err := client.Do(httpReq)
    if err != nil {
        c.JSON(500, gin.H{"error": "Failed to create ticket"})
        return
    }
    defer resp.Body.Close()
    
    var result map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&result)
    c.JSON(resp.StatusCode, result)
}
```

---

## Common Pitfalls to Avoid

### 1. Hardcoding API Keys

❌ **Bad:**
```javascript
const API_KEY = 'tl_live_abc123';
```

✅ **Good:**
```javascript
const API_KEY = process.env.TURADESK_API_KEY;
```

### 2. Missing Required Fields

❌ **Bad:** Sending ticket without subject or email
```javascript
body: JSON.stringify({ body: "Help!" })
```

✅ **Good:**
```javascript
body: JSON.stringify({
  subject: "Support request",
  body: "Help!",
  email: "user@example.com",
})
```

### 3. Not Handling Pagination

❌ **Bad:** Assuming all tickets fit in one response
```javascript
const data = await fetch('/tickets').then(r => r.json());
// data.tickets might be truncated
```

✅ **Good:** Use `nextCursor` and `hasMore` for pagination
```javascript
let cursor = null;
let allTickets = [];
do {
  const url = cursor
    ? `/tickets?cursor=${cursor}&limit=100`
    : '/tickets?limit=100';
  const data = await fetch(url).then(r => r.json());
  allTickets = allTickets.concat(data.tickets);
  cursor = data.hasMore ? data.nextCursor : null;
} while (cursor);
```

### 4. Exposing API Key to Frontend

❌ **Bad:** Calling TuraDesk API directly from browser
```javascript
// In React component - NEVER do this
fetch('https://www.turadesk.com/api/v1/desk/tickets', {
  headers: { 'Authorization': `Bearer ${API_KEY}` }
});
```

✅ **Good:** Proxy through your backend
```javascript
// Frontend calls YOUR API
fetch('/api/support/tickets', {
  method: 'POST',
  body: JSON.stringify({ subject, body, email }),
});
```

---

## Completion Checklist

Before considering the integration complete:

- [ ] Environment variables added (`.env`, `.env.example`)
- [ ] Create ticket endpoint implemented
- [ ] List tickets endpoint implemented (with filters)
- [ ] Get ticket detail endpoint implemented
- [ ] Add comment endpoint implemented (if needed)
- [ ] Update/close endpoints (if needed)
- [ ] Error handling in place
- [ ] API key never exposed to frontend
- [ ] Pagination handled for list endpoint
- [ ] Instructions for getting API key provided

---

**For more details, see:**
- [SKILL.md](https://www.turadesk.com/SKILL.md) — Complete API reference
- [API.md](https://www.turadesk.com/API.md) — Detailed endpoint docs
- [QUICKSTART.md](https://www.turadesk.com/QUICKSTART.md) — 5-minute integration

**Built for AI agents by developers who love automation.** 🤖
