# Forgejo Issue Attachments API

Base URL: `https://git.cleverthis.com/api/v1`

All endpoints require authentication via `Authorization: token ${FORGEJO_PAT}` header.

---

## Server Attachment Settings

Before uploading, you can check what the server allows:

```
GET /settings/attachment
```

This is a **public endpoint** (no auth required) that returns:

| Field | Type | Description |
|-------|------|-------------|
| `allowed_types` | string | Comma-separated MIME types or extensions allowed (e.g. `.docx,.pptx,.xlsx,image/*,video/*`) |
| `enabled` | boolean | Whether attachments are enabled |
| `max_size` | integer | Maximum file size in MB |
| `max_files` | integer | Maximum number of files per upload |

```bash
# Check attachment settings
curl -s "${FORGEJO_URL}/api/v1/settings/attachment"

# Example response:
# {
#   "allowed_types": ".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip",
#   "enabled": true,
#   "max_files": 5,
#   "max_size": 4
# }
```

---

## Attachment Object Fields

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | Attachment ID |
| `name` | string | File name |
| `size` | integer | File size in bytes |
| `download_count` | integer | Number of times downloaded |
| `created_at` | string | ISO 8601 creation timestamp |
| `uuid` | string | Unique attachment identifier |
| `browser_download_url` | string | URL to download the file |

---

## Issue Attachments

### List Issue Attachments

```
GET /repos/{owner}/{repo}/issues/{index}/assets
```

Returns all attachments on the specified issue.

#### Parameters

| Parameter | Type | In | Description |
|-----------|------|------|-------------|
| `owner` | string | path | **Required.** Repository owner |
| `repo` | string | path | **Required.** Repository name |
| `index` | integer | path | **Required.** Issue index number |
| `page` | integer | query | Page number (1-based). Default: `1` |
| `limit` | integer | query | Page size |

#### Response

Returns `200` with an array of Attachment objects.

#### Examples

```bash
# List all attachments on issue #42
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/assets"

# List with file details
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/assets" \
  | jq '[.[] | {id, name, size, download_count, browser_download_url}]'
```

---

### Create an Issue Attachment

```
POST /repos/{owner}/{repo}/issues/{index}/assets
```

Uploads a file attachment to an issue. Uses `multipart/form-data` encoding.

#### Parameters

| Parameter | Type | In | Description |
|-----------|------|------|-------------|
| `owner` | string | path | **Required.** Repository owner |
| `repo` | string | path | **Required.** Repository name |
| `index` | integer | path | **Required.** Issue index number |
| `name` | string | query | Custom file name (optional, defaults to the uploaded filename) |
| `attachment` | file | body | **Required.** The file to upload (multipart form field named `attachment`) |

#### Response

Returns `201` with the created Attachment object.

#### Examples

```bash
# Upload a file to issue #42
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -F "attachment=@{/path/to/file}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/assets"

# Upload with a custom name
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -F "attachment=@{/path/to/file}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/assets?name={filename}"

# Upload a PDF
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -F "attachment=@{/path/to/file}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/assets"

# Upload a log file
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -F "attachment=@{/path/to/file}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/assets?name={filename}"

# Upload a zip archive
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -F "attachment=@{/path/to/file}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/assets"

# Upload and capture the attachment ID
ATTACHMENT_ID=$(curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -F "attachment=@{/path/to/file}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/assets" \
  | jq '.id')
echo "Attachment ID: ${ATTACHMENT_ID}"
```

---

### Get a Specific Issue Attachment

```
GET /repos/{owner}/{repo}/issues/{index}/assets/{attachment_id}
```

Returns metadata for a specific attachment.

#### Parameters

| Parameter | Type | In | Description |
|-----------|------|------|-------------|
| `owner` | string | path | **Required.** Repository owner |
| `repo` | string | path | **Required.** Repository name |
| `index` | integer | path | **Required.** Issue index number |
| `attachment_id` | integer | path | **Required.** Attachment ID |

#### Response

Returns `200` with an Attachment object.

#### Examples

```bash
# Get attachment metadata
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/assets/{asset_id}"

# Get the download URL
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/assets/{asset_id}" \
  | jq -r '.browser_download_url'
```

---

### Edit an Issue Attachment

```
PATCH /repos/{owner}/{repo}/issues/{index}/assets/{attachment_id}
```

Renames an attachment.

#### Parameters

| Parameter | Type | In | Description |
|-----------|------|------|-------------|
| `owner` | string | path | **Required.** Repository owner |
| `repo` | string | path | **Required.** Repository name |
| `index` | integer | path | **Required.** Issue index number |
| `attachment_id` | integer | path | **Required.** Attachment ID |

#### Request Body — `EditAttachmentOptions`

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | No | New file name |

#### Response

Returns `201` with the updated Attachment object.

#### Example

```bash
# Rename an attachment
curl -s -X PATCH \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "screenshot-login-bug-v2.png"
  }' \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/assets/{asset_id}"
```

---

### Delete an Issue Attachment

```
DELETE /repos/{owner}/{repo}/issues/{index}/assets/{attachment_id}
```

Permanently deletes an attachment.

#### Parameters

| Parameter | Type | In | Description |
|-----------|------|------|-------------|
| `owner` | string | path | **Required.** Repository owner |
| `repo` | string | path | **Required.** Repository name |
| `index` | integer | path | **Required.** Issue index number |
| `attachment_id` | integer | path | **Required.** Attachment ID |

#### Response

Returns `204` (No Content) on success.

#### Example

```bash
# Delete attachment
curl -s -X DELETE \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/assets/{asset_id}"
```

---

## Comment Attachments

### List Comment Attachments

```
GET /repos/{owner}/{repo}/issues/comments/{id}/assets
```

Returns all attachments on the specified comment.

#### Parameters

| Parameter | Type | In | Description |
|-----------|------|------|-------------|
| `owner` | string | path | **Required.** Repository owner |
| `repo` | string | path | **Required.** Repository name |
| `id` | integer | path | **Required.** Comment ID |
| `page` | integer | query | Page number (1-based). Default: `1` |
| `limit` | integer | query | Page size |

#### Response

Returns `200` with an array of Attachment objects.

#### Example

```bash
# List attachments on comment #789
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/comments/{comment_id}/assets"
```

---

### Add a Comment Attachment

```
POST /repos/{owner}/{repo}/issues/comments/{id}/assets
```

Uploads a file attachment to a comment. Uses `multipart/form-data` encoding.

#### Parameters

| Parameter | Type | In | Description |
|-----------|------|------|-------------|
| `owner` | string | path | **Required.** Repository owner |
| `repo` | string | path | **Required.** Repository name |
| `id` | integer | path | **Required.** Comment ID |
| `name` | string | query | Custom file name (optional) |
| `attachment` | file | body | **Required.** The file to upload (multipart form field named `attachment`) |

#### Response

Returns `201` with the created Attachment object.

#### Examples

```bash
# Upload a file to comment #789
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -F "attachment=@{/path/to/file}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/comments/{comment_id}/assets"

# Upload with custom name
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -F "attachment=@{/path/to/file}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/comments/{comment_id}/assets?name={filename}"
```

---

### Get a Specific Comment Attachment

```
GET /repos/{owner}/{repo}/issues/comments/{id}/assets/{attachment_id}
```

Returns metadata for a specific comment attachment.

#### Parameters

| Parameter | Type | In | Description |
|-----------|------|------|-------------|
| `owner` | string | path | **Required.** Repository owner |
| `repo` | string | path | **Required.** Repository name |
| `id` | integer | path | **Required.** Comment ID |
| `attachment_id` | integer | path | **Required.** Attachment ID |

#### Response

Returns `200` with an Attachment object.

#### Example

```bash
# Get comment attachment metadata
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/comments/{comment_id}/assets/{asset_id}"
```

---

### Edit a Comment Attachment

```
PATCH /repos/{owner}/{repo}/issues/comments/{id}/assets/{attachment_id}
```

Renames a comment attachment.

#### Parameters

| Parameter | Type | In | Description |
|-----------|------|------|-------------|
| `owner` | string | path | **Required.** Repository owner |
| `repo` | string | path | **Required.** Repository name |
| `id` | integer | path | **Required.** Comment ID |
| `attachment_id` | integer | path | **Required.** Attachment ID |

#### Request Body — `EditAttachmentOptions`

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | No | New file name |

#### Response

Returns `201` with the updated Attachment object.

#### Example

```bash
# Rename comment attachment
curl -s -X PATCH \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "updated-screenshot.png"
  }' \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/comments/{comment_id}/assets/{asset_id}"
```

---

### Delete a Comment Attachment

```
DELETE /repos/{owner}/{repo}/issues/comments/{id}/assets/{attachment_id}
```

Permanently deletes a comment attachment.

#### Parameters

| Parameter | Type | In | Description |
|-----------|------|------|-------------|
| `owner` | string | path | **Required.** Repository owner |
| `repo` | string | path | **Required.** Repository name |
| `id` | integer | path | **Required.** Comment ID |
| `attachment_id` | integer | path | **Required.** Attachment ID |

#### Response

Returns `204` (No Content) on success.

#### Example

```bash
# Delete comment attachment
curl -s -X DELETE \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/comments/{comment_id}/assets/{asset_id}"
```

---

## Common Patterns

### Upload a file and reference it in a comment

```bash
# 1. Upload file to issue
UPLOAD_RESPONSE=$(curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -F "attachment=@{/path/to/file}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/assets")

DOWNLOAD_URL=$(echo "$UPLOAD_RESPONSE" | jq -r '.browser_download_url')
FILENAME=$(echo "$UPLOAD_RESPONSE" | jq -r '.name')

# 2. Add a comment referencing the attachment
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d "{
    \"body\": \"Here is the screenshot showing the bug:\\n\\n![${FILENAME}](${DOWNLOAD_URL})\"
  }" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/comments"
```

### Download an attachment

```bash
# Get the download URL
DOWNLOAD_URL=$(curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/assets/{asset_id}" \
  | jq -r '.browser_download_url')

# Download the file
curl -s -L \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -o "downloaded-file.png" \
  "${DOWNLOAD_URL}"
```

### List all attachments across all issues (iterate)

```bash
# Get all issues then fetch attachments for each
ISSUES=$(curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues?state=all&limit=50" \
  | jq -r '.[].number')

for INDEX in $ISSUES; do
  ASSETS=$(curl -s \
    -H "Authorization: token ${FORGEJO_PAT}" \
    "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/${INDEX}/assets")
  COUNT=$(echo "$ASSETS" | jq 'length')
  if [ "$COUNT" -gt 0 ]; then
    echo "Issue #${INDEX}: ${COUNT} attachment(s)"
    echo "$ASSETS" | jq -r '.[] | "  - \(.name) (\(.size) bytes)"'
  fi
done
```
