# Support tickets

Open support tickets and reply to them.

## Ticket categories

`GET /tickets/queues` · scope `tickets:read`

The categories (queues) a ticket can be opened in.

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/tickets/queues" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/tickets/queues", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/tickets/queues", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": [
    {
      "slug": "technical-problems",
      "title": "Technical Problems"
    }
  ]
}
```

Errors: `rate_limited`, `scope_missing`, `unauthorized`

## List tickets

`GET /tickets` · scope `tickets:read`

Your tickets, newest first.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `status` | query | string (one of: open, closed) | no | Only open or only closed tickets. |
| `limit` | query | integer | no | Items per page (1-200). Default: `50`. |
| `cursor` | query | string | no | The next_cursor value of the previous page. Omit for the first page. |

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/tickets?status=open" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/tickets?status=open", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/tickets?status=open", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": [
    {
      "id": 4412,
      "reference": "general-support-request-4412",
      "title": "Site shows error 500",
      "queue": "General Support Request",
      "status": "open",
      "priority": "normal",
      "created_at": "2026-09-20T08:00:00Z",
      "updated_at": "2026-09-20T08:00:00Z",
      "panel_url": "https://app.pbn.ltd/account/tickets"
    }
  ],
  "next_cursor": null,
  "has_more": false
}
```

Errors: `rate_limited`, `scope_missing`, `unauthorized`

## Read a ticket

`GET /tickets/{ticket_id}` · scope `tickets:read`

A ticket with every public message (yours and support's). can_reply says if you may add a message now (ticket limits: one message before support answers).

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `ticket_id` | path | integer | yes | The ticket id. |

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/tickets/4412" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/tickets/4412", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/tickets/4412", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "id": 4412,
    "reference": "general-support-request-4412",
    "title": "Site shows error 500",
    "queue": "General Support Request",
    "status": "open",
    "priority": "normal",
    "created_at": "2026-09-20T08:00:00Z",
    "updated_at": "2026-09-20T08:00:00Z",
    "panel_url": "https://app.pbn.ltd/account/tickets",
    "resolution": null,
    "can_reply": true,
    "can_reply_reason": null,
    "messages": [
      {
        "at": "2026-09-20T08:00:00Z",
        "from": "you",
        "text": "My site shows error 500 since this morning."
      },
      {
        "at": "2026-09-20T08:40:00Z",
        "from": "support",
        "text": "We are looking at it now.",
        "attachments": []
      }
    ]
  }
}
```

Errors: `not_found`, `rate_limited`, `scope_missing`, `unauthorized`

## Open a ticket

`POST /tickets` · scope `tickets:write`

Opens a ticket (support is e-mailed as for a panel ticket). The ticket limits apply: at most 2 open tickets at a time (409 ticket_limit).

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `title` | body | string | yes | Short summary. |
| `message` | body | string | yes | What happened, what you expected. |
| `queue` | body | string | no | Category slug (GET /tickets/queues). Default: `general-support-request`. |
| `priority` | body | string (one of: low, normal, high) | no | Priority. Default: `normal`. |
| `site_id` | body | integer | no | The site it is about (its domain is added to the ticket). |

### Example

```bash
curl -s -X POST "https://app.pbn.ltd/api/v1/tickets" \
  -H "Authorization: Bearer $PBN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title": "Question about my site", "message": "How do I add a subdomain?", "queue": "general-support-request", "site_id": 123}'
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/tickets", headers=headers, json={"title": "Question about my site", "message": "How do I add a subdomain?", "queue": "general-support-request", "site_id": 123}, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/tickets", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`, "Content-Type": "application/json"},
  body: JSON.stringify({"title": "Question about my site", "message": "How do I add a subdomain?", "queue": "general-support-request", "site_id": 123})
});
console.log(res.status, await res.json());
```

### Response

`201`

```json
{
  "data": {
    "id": 4412,
    "reference": "general-support-request-4412",
    "title": "Site shows error 500",
    "queue": "General Support Request",
    "status": "open",
    "priority": "normal",
    "created_at": "2026-09-20T08:00:00Z",
    "updated_at": "2026-09-20T08:00:00Z",
    "panel_url": "https://app.pbn.ltd/account/tickets"
  }
}
```

Errors: `conflict`, `rate_limited`, `scope_missing`, `unauthorized`, `validation_failed`

## Reply to a ticket

`POST /tickets/{ticket_id}/messages` · scope `tickets:write`

Adds your message to the ticket (support is notified). One message until support answers.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `ticket_id` | path | integer | yes | The ticket id. |
| `message` | body | string | yes | Your message. |

### Example

```bash
curl -s -X POST "https://app.pbn.ltd/api/v1/tickets/4412/messages" \
  -H "Authorization: Bearer $PBN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "Thanks, that worked."}'
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/tickets/4412/messages", headers=headers, json={"message": "Thanks, that worked."}, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/tickets/4412/messages", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`, "Content-Type": "application/json"},
  body: JSON.stringify({"message": "Thanks, that worked."})
});
console.log(res.status, await res.json());
```

### Response

`201`

```json
{
  "data": {
    "id": 4412,
    "reference": "general-support-request-4412",
    "title": "Site shows error 500",
    "queue": "General Support Request",
    "status": "open",
    "priority": "normal",
    "created_at": "2026-09-20T08:00:00Z",
    "updated_at": "2026-09-20T08:00:00Z",
    "panel_url": "https://app.pbn.ltd/account/tickets",
    "messages": []
  }
}
```

Errors: `conflict`, `not_found`, `rate_limited`, `scope_missing`, `unauthorized`
