> ## Documentation Index
> Fetch the complete documentation index at: https://docs.earthcoop.ir/llms.txt
> Use this file to discover all available pages before exploring further.

# Support Tickets API — Create, Update & Comment

> REST API endpoints for managing support tickets: create, list, view, update, close tickets, and add comments with optional file attachments.

The Tickets API lets you create and manage support tickets programmatically. Every ticket gets a unique tracking code (format: `TK-XXXXXXXX`) and automatic priority assignment. All endpoints under `/api/tickets` require a valid Bearer token.

***

## GET /api/tickets

Returns a paginated list of tickets belonging to the authenticated user. Tickets are matched by both user ID and email address, so tickets submitted via email that share your account email are also included.

**Authentication:** Required — `Authorization: Bearer {token}`

### Query Parameters

<ParamField query="status" type="string">
  Filter by ticket status. Accepted values: `open`, `in-progress`, `closed`.
</ParamField>

<ParamField query="priority" type="string">
  Filter by priority. Accepted values: `low`, `normal`, `high`.
</ParamField>

<ParamField query="category" type="string">
  Filter by category string (exact match).
</ParamField>

<ParamField query="q" type="string">
  Search term matched against the tracking code, subject, and message body.
</ParamField>

<ParamField query="per_page" type="integer">
  Number of tickets per page. Defaults to `15`.
</ParamField>

### Response Fields

<ResponseField name="success" type="boolean">
  `true` on success.
</ResponseField>

<ResponseField name="data" type="array">
  Array of ticket summary objects for the current page.
</ResponseField>

<ResponseField name="data[].id" type="integer">
  Unique ticket ID.
</ResponseField>

<ResponseField name="data[].tracking_code" type="string">
  Human-readable tracking code, e.g. `TK-A3BF92C1`.
</ResponseField>

<ResponseField name="data[].subject" type="string">
  Ticket subject line.
</ResponseField>

<ResponseField name="data[].status" type="string">
  Current status: `open`, `in-progress`, or `closed`.
</ResponseField>

<ResponseField name="data[].priority" type="string">
  Priority level: `low`, `normal`, or `high`.
</ResponseField>

<ResponseField name="data[].category" type="string">
  Optional category string, or `null`.
</ResponseField>

<ResponseField name="data[].assignee" type="object">
  The staff member assigned to the ticket, or `null`. Contains `id`, `first_name`, `last_name`.
</ResponseField>

<ResponseField name="data[].tags" type="array">
  Array of tag objects with `id` and `name`.
</ResponseField>

<ResponseField name="data[].created_at" type="string">
  ISO 8601 creation timestamp.
</ResponseField>

<ResponseField name="meta" type="object">
  Pagination metadata: `current_page`, `last_page`, `per_page`, `total`.
</ResponseField>

### Example Request

```bash theme={null}
curl -X GET "https://your-domain.com/api/tickets?status=open&per_page=10" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
```

### Example Response

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": 34,
      "tracking_code": "TK-A3BF92C1",
      "subject": "Unable to upload project documents",
      "status": "open",
      "priority": "high",
      "category": "technical",
      "assignee": {
        "id": 5,
        "first_name": "Sara",
        "last_name": "Tehrani"
      },
      "tags": [
        { "id": 2, "name": "upload" }
      ],
      "created_at": "2024-11-14T08:30:00Z"
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 3,
    "per_page": 10,
    "total": 27
  }
}
```

### Error Responses

**Unauthenticated — 401:**

```json theme={null}
{
  "message": "Unauthenticated."
}
```

***

## GET /api/tickets/stats

Returns a summary count of your tickets broken down by status. Useful for dashboard widgets.

**Authentication:** Required — `Authorization: Bearer {token}`

**Request:** No parameters.

### Response Fields

<ResponseField name="success" type="boolean">
  `true` on success.
</ResponseField>

<ResponseField name="data.total" type="integer">
  Total number of tickets associated with your account.
</ResponseField>

<ResponseField name="data.open" type="integer">
  Tickets with status `open`.
</ResponseField>

<ResponseField name="data.in_progress" type="integer">
  Tickets with status `in-progress`.
</ResponseField>

<ResponseField name="data.closed" type="integer">
  Tickets with status `closed`.
</ResponseField>

### Example Request

```bash theme={null}
curl -X GET https://your-domain.com/api/tickets/stats \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
```

### Example Response

```json theme={null}
{
  "success": true,
  "data": {
    "total": 27,
    "open": 12,
    "in_progress": 4,
    "closed": 11
  }
}
```

***

## POST /api/tickets

Creates a new support ticket. The system automatically assigns a unique tracking code and calculates a priority if you do not specify one. A confirmation email is sent to the authenticated user's email address upon creation.

**Authentication:** Required — `Authorization: Bearer {token}`

### Request Body

<ParamField body="subject" type="string" required>
  A clear, concise summary of the issue. Maximum 255 characters.
</ParamField>

<ParamField body="message" type="string" required>
  Detailed description of the issue. Minimum 10 characters.
</ParamField>

<ParamField body="priority" type="string">
  Requested priority: `low`, `normal`, or `high`. If omitted, the platform assigns one automatically based on the subject and message content.
</ParamField>

<ParamField body="category" type="string">
  Optional category string to help route the ticket.
</ParamField>

<ParamField body="tags" type="array">
  Optional array of tag IDs (integers) to attach to the ticket.
</ParamField>

<ParamField body="attachments" type="array">
  Optional array of file uploads. Each file must be under 10 MB. Accepted types: `jpg`, `jpeg`, `png`, `gif`, `pdf`, `doc`, `docx`, `txt`, `zip`, `rar`.
</ParamField>

### Response Fields

<ResponseField name="success" type="boolean">
  `true` when the ticket is created.
</ResponseField>

<ResponseField name="message" type="string">
  Confirmation message.
</ResponseField>

<ResponseField name="data" type="object">
  The full newly created ticket object including tracking code, assignee, tags, and attachments.
</ResponseField>

<ResponseField name="data.id" type="integer">
  Ticket ID.
</ResponseField>

<ResponseField name="data.tracking_code" type="string">
  Unique tracking code, e.g. `TK-B7CE41F2`.
</ResponseField>

<ResponseField name="data.status" type="string">
  Always `open` for a newly created ticket.
</ResponseField>

<ResponseField name="data.priority" type="string">
  Assigned priority: `low`, `normal`, or `high`.
</ResponseField>

<ResponseField name="data.sla_deadline" type="string">
  ISO 8601 timestamp of the resolution deadline.
</ResponseField>

### Example Request

<CodeGroup>
  ```bash cURL (JSON, no attachments) theme={null}
  curl -X POST https://your-domain.com/api/tickets \
    -H "Authorization: Bearer YOUR_TOKEN" \
    -H "Accept: application/json" \
    -H "Content-Type: application/json" \
    -d '{
      "subject": "Unable to upload project documents",
      "message": "When I try to upload a PDF on the project creation form the page shows a 500 error after selecting the file.",
      "priority": "high",
      "category": "technical"
    }'
  ```

  ```bash cURL (multipart, with attachment) theme={null}
  curl -X POST https://your-domain.com/api/tickets \
    -H "Authorization: Bearer YOUR_TOKEN" \
    -H "Accept: application/json" \
    -F "subject=Unable to upload project documents" \
    -F "message=When I try to upload a PDF the page shows a 500 error." \
    -F "priority=high" \
    -F "attachments[]=@/path/to/screenshot.png"
  ```
</CodeGroup>

### Example Response

```json theme={null}
{
  "success": true,
  "message": "Ticket created successfully.",
  "data": {
    "id": 35,
    "tracking_code": "TK-B7CE41F2",
    "subject": "Unable to upload project documents",
    "message": "When I try to upload a PDF on the project creation form...",
    "status": "open",
    "priority": "high",
    "category": "technical",
    "sla_deadline": "2024-11-16T08:30:00Z",
    "assignee": null,
    "tags": [],
    "attachments": [],
    "created_at": "2024-11-14T08:30:00Z"
  }
}
```

### Error Responses

**Validation failure — 422:**

```json theme={null}
{
  "success": false,
  "message": "Validation error.",
  "errors": {
    "subject": ["The subject field is required."],
    "message": ["The message must be at least 10 characters."]
  }
}
```

***

## GET /api/tickets/{id}

Returns full details for a single ticket, including the complete comment thread, all attachments, tags, and activity log. You may only view tickets associated with your account.

**Authentication:** Required — `Authorization: Bearer {token}`

### Path Parameters

<ParamField path="id" type="integer" required>
  The ticket ID to retrieve.
</ParamField>

### Response Fields

<ResponseField name="success" type="boolean">
  `true` on success.
</ResponseField>

<ResponseField name="data" type="object">
  The full ticket object.
</ResponseField>

<ResponseField name="data.comments" type="array">
  Chronologically ordered array of comment objects, each containing `id`, `user`, `message`, `attachments`, and `created_at`.
</ResponseField>

<ResponseField name="data.attachments" type="array">
  Array of attachment objects: `id`, `file_name`, `file_type`, `file_size`, `created_at`.
</ResponseField>

<ResponseField name="data.activities" type="array">
  Audit trail of status changes and field updates.
</ResponseField>

### Example Request

```bash theme={null}
curl -X GET https://your-domain.com/api/tickets/35 \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
```

### Example Response

```json theme={null}
{
  "success": true,
  "data": {
    "id": 35,
    "tracking_code": "TK-B7CE41F2",
    "subject": "Unable to upload project documents",
    "message": "When I try to upload a PDF...",
    "status": "open",
    "priority": "high",
    "category": "technical",
    "sla_deadline": "2024-11-16T08:30:00Z",
    "assignee": { "id": 5, "first_name": "Sara", "last_name": "Tehrani", "email": "sara@example.com" },
    "user": { "id": 12, "first_name": "Ali", "last_name": "Mohammadi", "email": "ali@example.com" },
    "comments": [],
    "tags": [],
    "attachments": [],
    "activities": [],
    "created_at": "2024-11-14T08:30:00Z"
  }
}
```

### Error Responses

**Ticket not found or not owned by you — 404:**

```json theme={null}
{
  "message": "No query results for model [Ticket] 35."
}
```

***

## PUT /api/tickets/{id}

Updates the `priority` and/or `category` of an open ticket. Closed tickets cannot be edited. The resolution deadline is recalculated automatically when priority changes.

**Authentication:** Required — `Authorization: Bearer {token}`

### Path Parameters

<ParamField path="id" type="integer" required>
  The ticket ID to update.
</ParamField>

### Request Body

<ParamField body="priority" type="string">
  New priority. One of: `low`, `normal`, `high`.
</ParamField>

<ParamField body="category" type="string">
  New category string. Pass `null` to clear the category.
</ParamField>

### Example Request

```bash theme={null}
curl -X PUT https://your-domain.com/api/tickets/35 \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"priority": "normal"}'
```

### Example Response

```json theme={null}
{
  "success": true,
  "message": "Ticket updated successfully.",
  "data": {
    "id": 35,
    "tracking_code": "TK-B7CE41F2",
    "priority": "normal",
    "sla_deadline": "2024-11-18T08:30:00Z",
    "status": "open"
  }
}
```

### Error Responses

**Attempting to edit a closed ticket — 400:**

```json theme={null}
{
  "success": false,
  "message": "Cannot edit a closed ticket."
}
```

**Validation failure — 422:**

```json theme={null}
{
  "success": false,
  "message": "Validation error.",
  "errors": {
    "priority": ["The selected priority is invalid."]
  }
}
```

***

## PUT /api/tickets/{id}/close

Closes an open ticket and records the resolution timestamp. Once closed, the ticket cannot be edited, but adding a new comment will automatically reopen it.

**Authentication:** Required — `Authorization: Bearer {token}`

### Path Parameters

<ParamField path="id" type="integer" required>
  The ticket ID to close.
</ParamField>

**Request:** No body required.

### Example Request

```bash theme={null}
curl -X PUT https://your-domain.com/api/tickets/35/close \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
```

### Example Response

```json theme={null}
{
  "success": true,
  "message": "Ticket closed successfully.",
  "data": {
    "id": 35,
    "status": "closed",
    "resolved_at": "2024-11-15T14:22:00Z"
  }
}
```

### Error Responses

**Ticket is already closed — 400:**

```json theme={null}
{
  "success": false,
  "message": "Ticket is already closed."
}
```

***

## POST /api/tickets/{id}/comments

Adds a comment to an existing ticket. If the ticket was closed, adding a comment automatically reopens it. Attachments may be uploaded together with the comment as multipart form data.

**Authentication:** Required — `Authorization: Bearer {token}`

### Path Parameters

<ParamField path="id" type="integer" required>
  The ticket ID to comment on.
</ParamField>

### Request Body

<ParamField body="message" type="string" required>
  Comment text. Minimum 5 characters.
</ParamField>

<ParamField body="attachments" type="array">
  Optional array of file uploads. Each file must be under 10 MB. Accepted types: `jpg`, `jpeg`, `png`, `gif`, `pdf`, `doc`, `docx`, `txt`, `zip`, `rar`.
</ParamField>

### Response Fields

<ResponseField name="success" type="boolean">
  `true` when the comment is saved.
</ResponseField>

<ResponseField name="message" type="string">
  Confirmation message.
</ResponseField>

<ResponseField name="data" type="object">
  The new comment object, including `id`, `ticket_id`, `user`, `message`, `attachments`, and `created_at`.
</ResponseField>

### Example Request

```bash theme={null}
curl -X POST https://your-domain.com/api/tickets/35/comments \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"message": "I have tried clearing my browser cache and the error persists."}'
```

### Example Response

```json theme={null}
{
  "success": true,
  "message": "Comment added successfully.",
  "data": {
    "id": 88,
    "ticket_id": 35,
    "message": "I have tried clearing my browser cache and the error persists.",
    "user": { "id": 12, "first_name": "Ali", "last_name": "Mohammadi" },
    "attachments": [],
    "created_at": "2024-11-15T10:05:00Z"
  }
}
```

### Error Responses

**Validation failure — 422:**

```json theme={null}
{
  "success": false,
  "message": "Validation error.",
  "errors": {
    "message": ["The message must be at least 5 characters."]
  }
}
```

***

## GET /api/tickets/{id}/attachments/{attachment_id}/download

Downloads a file attachment associated with a ticket. Returns the raw file with appropriate `Content-Disposition` and `Content-Type` headers. You may only download attachments on tickets associated with your account.

**Authentication:** Required — `Authorization: Bearer {token}`

### Path Parameters

<ParamField path="id" type="integer" required>
  The ticket ID that owns the attachment.
</ParamField>

<ParamField path="attachment_id" type="integer" required>
  The attachment ID to download.
</ParamField>

### Response

On success the response body is the raw file binary (not JSON). Use the `Content-Disposition` header to obtain the original file name.

### Example Request

```bash theme={null}
curl -X GET https://your-domain.com/api/tickets/35/attachments/14/download \
  -H "Authorization: Bearer YOUR_TOKEN" \
  --output screenshot.png
```

### Error Responses

**File no longer exists on the server — 404:**

```json theme={null}
{
  "success": false,
  "message": "File not found."
}
```
