Claude-skill-registry atlassian-attachments
Attach documents, screenshots, PDFs, and files to Jira issues and Confluence pages via REST API. Use when uploading evidence, documentation, or media to Atlassian products.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/atlassian-attachments" ~/.claude/skills/majiayu000-claude-skill-registry-atlassian-attachments && rm -rf "$T"
skills/data/atlassian-attachments/SKILL.mdAtlassian Attachments Skill
Attach files, screenshots, and documents to Jira issues and Confluence pages using the Atlassian REST API.
Authentication Setup
API Token (Required)
Generate an API token at: https://id.atlassian.com/manage-profile/security/api-tokens
Environment Variables
export ATLASSIAN_DOMAIN="your-domain" export ATLASSIAN_EMAIL="your-email@example.com" export ATLASSIAN_API_TOKEN="your-api-token"
Setup via .envrc (Recommended)
If environment variables are not set, add them to
.envrc in your project root for automatic loading with direnv:
# .envrc export ATLASSIAN_DOMAIN="your-domain" export ATLASSIAN_EMAIL="your-email@example.com" export ATLASSIAN_API_TOKEN="your-api-token"
Then allow the file:
direnv allow
Security Note: Add
.envrc to .gitignore to prevent committing credentials:
echo ".envrc" >> .gitignore
Check Environment Setup
# Verify variables are set echo "Domain: ${ATLASSIAN_DOMAIN:-NOT SET}" echo "Email: ${ATLASSIAN_EMAIL:-NOT SET}" echo "Token: ${ATLASSIAN_API_TOKEN:+SET}"
If any show "NOT SET", prompt the user to configure
.envrc.
Base URLs
Jira: https://{domain}.atlassian.net/rest/api/3 Confluence: https://{domain}.atlassian.net/wiki/rest/api
Jira REST API - Upload Attachments
Endpoint
POST https://{domain}.atlassian.net/rest/api/3/issue/{issueIdOrKey}/attachments
Required Headers
| Header | Value | Purpose |
|---|---|---|
| | CSRF protection bypass (required) |
| | File upload format |
cURL Command
curl --location --request POST \ 'https://your-domain.atlassian.net/rest/api/3/issue/PROJ-123/attachments' \ -u 'email@example.com:<api_token>' \ -H 'X-Atlassian-Token: no-check' \ --form 'file=@"./screenshots/bug-evidence.png"'
Python Example
import requests from requests.auth import HTTPBasicAuth def upload_jira_attachment(domain, email, api_token, issue_key, file_path): url = f"https://{domain}.atlassian.net/rest/api/3/issue/{issue_key}/attachments" auth = HTTPBasicAuth(email, api_token) headers = { "X-Atlassian-Token": "no-check" } with open(file_path, 'rb') as file: files = {'file': file} response = requests.post(url, auth=auth, headers=headers, files=files) if response.status_code == 200: return response.json() else: raise Exception(f"Upload failed: {response.status_code} - {response.text}") # Usage result = upload_jira_attachment( domain="your-domain", email="your-email@example.com", api_token="your-api-token", issue_key="PROJ-123", file_path="./screenshots/bug-evidence.png" )
Bash Script - Multiple Files
#!/bin/bash DOMAIN="your-domain" EMAIL="your-email@example.com" API_TOKEN="your-api-token" ISSUE_KEY="PROJ-123" FILES_DIR="./qa-tests/screenshots/" for file in "$FILES_DIR"*.png; do echo "Uploading: $file" curl --silent --location --request POST \ "https://${DOMAIN}.atlassian.net/rest/api/3/issue/${ISSUE_KEY}/attachments" \ -u "${EMAIL}:${API_TOKEN}" \ -H "X-Atlassian-Token: no-check" \ --form "file=@\"$file\"" echo " Done" done
Confluence REST API - Upload Attachments
Endpoint
POST https://{domain}.atlassian.net/wiki/rest/api/content/{pageId}/child/attachment
Required Headers
| Header | Value | Purpose |
|---|---|---|
| | CSRF protection bypass (required) |
| | File upload format |
cURL Command - Basic Auth
curl -u "${USER_EMAIL}:${API_TOKEN}" \ -X POST \ -H "X-Atlassian-Token: nocheck" \ -F "file=@./diagram.png" \ -F "comment=Uploaded via REST API" \ "https://${DOMAIN}.atlassian.net/wiki/rest/api/content/${PAGE_ID}/child/attachment"
cURL Command - Personal Access Token
curl -X POST \ -H "Authorization: Bearer ${PAT_TOKEN}" \ -H "X-Atlassian-Token: no-check" \ -H "Content-Type: multipart/form-data" \ -F "file=@./document.pdf" \ "https://${DOMAIN}.atlassian.net/wiki/rest/api/content/${PAGE_ID}/child/attachment"
Python Example
import requests from requests.auth import HTTPBasicAuth def upload_confluence_attachment(domain, email, api_token, page_id, file_path, comment=""): url = f"https://{domain}.atlassian.net/wiki/rest/api/content/{page_id}/child/attachment" auth = HTTPBasicAuth(email, api_token) headers = { "X-Atlassian-Token": "nocheck" } with open(file_path, 'rb') as file: files = {'file': file} data = {'comment': comment} if comment else {} response = requests.post(url, auth=auth, headers=headers, files=files, data=data) if response.status_code in [200, 201]: return response.json() else: raise Exception(f"Upload failed: {response.status_code} - {response.text}") # Usage result = upload_confluence_attachment( domain="your-domain", email="your-email@example.com", api_token="your-api-token", page_id="123456789", file_path="./docs/architecture.png", comment="Architecture diagram v2" )
Get Page ID by Title
# Find page ID from title curl -u "${EMAIL}:${API_TOKEN}" \ "https://${DOMAIN}.atlassian.net/wiki/rest/api/content?title=Page%20Title&spaceKey=SPACE" \ | jq '.results[0].id'
Embed Attachment in Page Content
After uploading, the attachment is in the page's attachment list but NOT embedded in content. You must update the page body to display it.
Important: Use Markdown, Not Wiki Markup
| Format | Syntax | Works with API? |
|---|---|---|
| Wiki markup | | NO - Not converted |
| Markdown | | YES - Converted to storage format |
| Storage format | | YES - Native format |
Key insight: Markdown image syntax gets automatically converted to Confluence storage format when using the API.
CRITICAL: Storage Format for Attachments vs External URLs
When using storage format directly, you MUST use the correct element for referencing attachments:
| Reference Type | Element | Use Case |
|---|---|---|
| Page attachment | | Files uploaded to the page |
| External URL | | External image URLs |
WRONG - Using ri:url for attachments (images won't display):
<ac:image ac:src="screenshot.png"> <ri:url ri:value="screenshot.png" /> </ac:image>
CORRECT - Using ri:attachment for uploaded files:
<ac:image> <ri:attachment ri:filename="screenshot.png" /> </ac:image>
The
ri:url element is for external URLs only. For files uploaded as attachments to the page, you MUST use ri:attachment with ri:filename.
Method 1: Markdown (Recommended)
Use
representation: "wiki" with Markdown syntax:
curl -u "${EMAIL}:${API_TOKEN}" \ -X PUT \ -H "Content-Type: application/json" \ "https://${DOMAIN}.atlassian.net/wiki/rest/api/content/${PAGE_ID}" \ -d '{ "version": {"number": NEW_VERSION}, "title": "Page Title", "type": "page", "body": { "wiki": { "value": "# My Page\n\nHere is the diagram:\n\n\n\nMore content here.", "representation": "wiki" } } }'
Method 2: Storage Format (Direct)
Use native Confluence storage format with
representation: "storage":
curl -u "${EMAIL}:${API_TOKEN}" \ -X PUT \ -H "Content-Type: application/json" \ "https://${DOMAIN}.atlassian.net/wiki/rest/api/content/${PAGE_ID}" \ -d '{ "version": {"number": NEW_VERSION}, "title": "Page Title", "type": "page", "body": { "storage": { "value": "<p>Here is the diagram:</p><ac:image ac:align=\"center\" ac:width=\"800\"><ri:attachment ri:filename=\"architecture.png\"/></ac:image>", "representation": "storage" } } }'
Storage Format Image Options
IMPORTANT: Always use
<ri:attachment ri:filename="..."/> for page attachments, never <ri:url>.
RECOMMENDED for QA screenshots: Use
ac:width="600" to prevent images from being too wide and hindering readability:
<!-- RECOMMENDED: Centered image with controlled width (best for QA screenshots) --> <ac:image ac:align="center" ac:layout="center" ac:width="600"> <ri:attachment ri:filename="screenshot.png"/> </ac:image> <!-- Basic image (attachment) - will be full width, may be too large --> <ac:image> <ri:attachment ri:filename="screenshot.png"/> </ac:image> <!-- Element screenshots (smaller width for UI elements) --> <ac:image ac:align="center" ac:layout="center" ac:width="400"> <ri:attachment ri:filename="button-element.png"/> </ac:image> <!-- Full-width diagram (use sparingly) --> <ac:image ac:align="center" ac:layout="center" ac:width="800"> <ri:attachment ri:filename="architecture-diagram.png"/> </ac:image> <!-- As thumbnail (clickable to expand) --> <ac:image ac:thumbnail="true"> <ri:attachment ri:filename="photo.jpg"/> </ac:image> <!-- With border --> <ac:image ac:border="true" ac:width="400"> <ri:attachment ri:filename="ui-mockup.png"/> </ac:image> <!-- External URL (only for images NOT uploaded as attachments) --> <ac:image> <ri:url ri:value="https://example.com/external-image.png"/> </ac:image>
Width Guidelines:
| Image Type | Recommended Width | Notes |
|---|---|---|
| Full page screenshots | | Readable without scrolling |
| Element screenshots | | Small UI components |
| Architecture diagrams | | Complex diagrams need more space |
| Thumbnails | Use | Clickable to expand |
Python Example - Upload and Embed
import requests from requests.auth import HTTPBasicAuth import json def upload_and_embed_image(domain, email, api_token, page_id, file_path, alt_text=""): base_url = f"https://{domain}.atlassian.net/wiki/rest/api" auth = HTTPBasicAuth(email, api_token) filename = file_path.split('/')[-1] # 1. Upload attachment upload_url = f"{base_url}/content/{page_id}/child/attachment" headers = {"X-Atlassian-Token": "nocheck"} with open(file_path, 'rb') as f: files = {'file': f} response = requests.post(upload_url, auth=auth, headers=headers, files=files) if response.status_code not in [200, 201]: raise Exception(f"Upload failed: {response.text}") # 2. Get current page version page_url = f"{base_url}/content/{page_id}?expand=body.wiki,version" page = requests.get(page_url, auth=auth).json() current_version = page['version']['number'] title = page['title'] # 3. Update page with embedded image using Markdown update_url = f"{base_url}/content/{page_id}" # Get existing content or start fresh existing_content = page.get('body', {}).get('wiki', {}).get('value', '') # Append image in Markdown format new_content = f"{existing_content}\n\n" payload = { "version": {"number": current_version + 1}, "title": title, "type": "page", "body": { "wiki": { "value": new_content, "representation": "wiki" } } } response = requests.put( update_url, auth=auth, headers={"Content-Type": "application/json"}, data=json.dumps(payload) ) if response.status_code == 200: return response.json() else: raise Exception(f"Update failed: {response.text}") # Usage upload_and_embed_image( domain="your-domain", email="your-email@example.com", api_token="your-api-token", page_id="123456789", file_path="./screenshots/feature-demo.png", alt_text="Feature Demo Screenshot" )
Complete Workflow Script
#!/bin/bash # upload-and-embed.sh - Upload image and embed in Confluence page DOMAIN="${ATLASSIAN_DOMAIN}" EMAIL="${ATLASSIAN_EMAIL}" API_TOKEN="${ATLASSIAN_API_TOKEN}" PAGE_ID="$1" FILE_PATH="$2" FILENAME=$(basename "$FILE_PATH") # 1. Upload the attachment echo "Uploading ${FILENAME}..." curl -s -u "${EMAIL}:${API_TOKEN}" \ -X POST \ -H "X-Atlassian-Token: nocheck" \ -F "file=@${FILE_PATH}" \ "https://${DOMAIN}.atlassian.net/wiki/rest/api/content/${PAGE_ID}/child/attachment" > /dev/null # 2. Get current page version VERSION=$(curl -s -u "${EMAIL}:${API_TOKEN}" \ "https://${DOMAIN}.atlassian.net/wiki/rest/api/content/${PAGE_ID}?expand=version" \ | jq '.version.number') TITLE=$(curl -s -u "${EMAIL}:${API_TOKEN}" \ "https://${DOMAIN}.atlassian.net/wiki/rest/api/content/${PAGE_ID}" \ | jq -r '.title') # 3. Update page with embedded image (Markdown format) NEW_VERSION=$((VERSION + 1)) echo "Embedding image in page (version ${NEW_VERSION})..." curl -s -u "${EMAIL}:${API_TOKEN}" \ -X PUT \ -H "Content-Type: application/json" \ "https://${DOMAIN}.atlassian.net/wiki/rest/api/content/${PAGE_ID}" \ -d "{ \"version\": {\"number\": ${NEW_VERSION}}, \"title\": \"${TITLE}\", \"type\": \"page\", \"body\": { \"wiki\": { \"value\": \"\", \"representation\": \"wiki\" } } }" > /dev/null echo "Done! Image embedded in page."
Supported File Types
Jira Attachments
| Category | Extensions | Max Size |
|---|---|---|
| Images | , , , , , | 10 MB |
| Documents | , , , , , , | 10 MB |
| Text | , , , , , | 10 MB |
| Archives | , , | 10 MB |
| Code | , , , , , | 10 MB |
Confluence Attachments
| Category | Extensions | Max Size |
|---|---|---|
| Images | , , , , | 25 MB |
| Documents | , , , , , , | 100 MB |
| Media | , , | 100 MB |
| Design | , , , | 100 MB |
Workflow Examples
Attach QA Screenshot to Jira Issue
# Single screenshot curl --location --request POST \ "https://${DOMAIN}.atlassian.net/rest/api/3/issue/BUG-456/attachments" \ -u "${EMAIL}:${API_TOKEN}" \ -H "X-Atlassian-Token: no-check" \ --form "file=@./qa-tests/screenshots/QA-20250105-001/error-state.png"
Upload All Test Evidence
#!/bin/bash ISSUE_KEY="$1" SCREENSHOT_DIR="$2" for file in "$SCREENSHOT_DIR"/*; do echo "Uploading: $(basename $file)" curl --silent --location --request POST \ "https://${DOMAIN}.atlassian.net/rest/api/3/issue/${ISSUE_KEY}/attachments" \ -u "${EMAIL}:${API_TOKEN}" \ -H "X-Atlassian-Token: no-check" \ --form "file=@\"$file\"" > /dev/null done echo "All files uploaded to ${ISSUE_KEY}"
Upload Documentation to Confluence Page
# Upload PDF to specific page PAGE_ID="123456789" curl -u "${EMAIL}:${API_TOKEN}" \ -X POST \ -H "X-Atlassian-Token: nocheck" \ -F "file=@./docs/api-specification.pdf" \ -F "comment=API Spec v3.0" \ "https://${DOMAIN}.atlassian.net/wiki/rest/api/content/${PAGE_ID}/child/attachment"
Attachment Naming Conventions
For QA Evidence
{test-id}-{description}-{timestamp}.{ext} Examples: - QA-20250105-001-login-error-20250105T143022.png - QA-20250105-001-form-validation-20250105T143045.png
For Bug Reports
{issue-key}-{description}.{ext} Examples: - BUG-123-stack-trace.txt - BUG-123-screenshot-before.png - BUG-123-screenshot-after.png
For Documentation
{feature}-{version}-{type}.{ext} Examples: - auth-flow-v2-diagram.png - api-v3-specification.pdf - deployment-guide-v1.docx
QA Screenshot to Confluence Mapping
Local QA Structure vs Confluence Attachments
The QA test screenshots use a specific local directory structure that needs mapping when uploading to Confluence.
Local Structure (product-design plugin)
qa-tests/ ├── active/ │ └── QA-20250105-001-login.md # Test document └── screenshots/ └── QA-20250105-001/ # Test ID folder ├── 01-initial-state.png ├── 02-form-filled.png ├── 03-success-state.png └── elements/ ├── login-button.png ├── email-field.png └── password-field.png
Confluence Attachment Names (Flattened)
When uploading to Confluence, flatten the structure with prefixed names:
| Local Path | Confluence Attachment Name |
|---|---|
| |
| |
| |
| |
Naming Rules
- Prefix with test-id: All screenshots get
prefix{test-id}- - Element prefix: Files from
getelements/
infix-elem- - No nested folders: Confluence attachments are flat
- Preserve sequence: Keep
,01-
numbering02-
Upload Script with Renaming
#!/bin/bash # upload-qa-screenshots-confluence.sh # Upload QA screenshots to Confluence with proper naming TEST_ID="$1" PAGE_ID="$2" LOCAL_DIR="qa-tests/screenshots/${TEST_ID}" # Upload main screenshots for file in "$LOCAL_DIR"/*.png; do filename=$(basename "$file") # Prefix with test-id new_name="${TEST_ID}-${filename}" echo "Uploading: $new_name" curl -s -u "${ATLASSIAN_EMAIL}:${ATLASSIAN_API_TOKEN}" \ -X POST \ -H "X-Atlassian-Token: nocheck" \ -F "file=@${file};filename=${new_name}" \ "https://${ATLASSIAN_DOMAIN}.atlassian.net/wiki/rest/api/content/${PAGE_ID}/child/attachment" done # Upload element screenshots with elem- infix if [ -d "$LOCAL_DIR/elements" ]; then for file in "$LOCAL_DIR/elements"/*.png; do filename=$(basename "$file") # Add elem- infix new_name="${TEST_ID}-elem-${filename}" echo "Uploading element: $new_name" curl -s -u "${ATLASSIAN_EMAIL}:${ATLASSIAN_API_TOKEN}" \ -X POST \ -H "X-Atlassian-Token: nocheck" \ -F "file=@${file};filename=${new_name}" \ "https://${ATLASSIAN_DOMAIN}.atlassian.net/wiki/rest/api/content/${PAGE_ID}/child/attachment" done fi echo "Done! All screenshots uploaded to page ${PAGE_ID}"
Python Upload with Mapping
import os import requests from requests.auth import HTTPBasicAuth from pathlib import Path def upload_qa_screenshots_to_confluence( domain: str, email: str, api_token: str, page_id: str, test_id: str, local_dir: str = "qa-tests/screenshots" ): """Upload QA screenshots with Confluence-compatible naming.""" base_url = f"https://{domain}.atlassian.net/wiki/rest/api" auth = HTTPBasicAuth(email, api_token) headers = {"X-Atlassian-Token": "nocheck"} screenshot_dir = Path(local_dir) / test_id uploaded = [] # Upload main screenshots for file in screenshot_dir.glob("*.png"): confluence_name = f"{test_id}-{file.name}" with open(file, 'rb') as f: files = {'file': (confluence_name, f, 'image/png')} response = requests.post( f"{base_url}/content/{page_id}/child/attachment", auth=auth, headers=headers, files=files ) if response.ok: uploaded.append({"local": str(file), "confluence": confluence_name}) # Upload element screenshots elements_dir = screenshot_dir / "elements" if elements_dir.exists(): for file in elements_dir.glob("*.png"): confluence_name = f"{test_id}-elem-{file.name}" with open(file, 'rb') as f: files = {'file': (confluence_name, f, 'image/png')} response = requests.post( f"{base_url}/content/{page_id}/child/attachment", auth=auth, headers=headers, files=files ) if response.ok: uploaded.append({"local": str(file), "confluence": confluence_name}) return uploaded # Generate mapping table for documentation def generate_mapping_table(uploaded: list) -> str: """Generate markdown table of local-to-confluence name mapping.""" lines = ["| Local Path | Confluence Name |", "|------------|-----------------|"] for item in uploaded: lines.append(f"| `{item['local']}` | `{item['confluence']}` |") return "\n".join(lines)
Updating Markdown References
After upload, update the QA test document to use Confluence attachment names:
Before (local references):
 
After (Confluence references):
 
Automated Reference Update Script
import re from pathlib import Path def update_qa_doc_for_confluence(qa_doc_path: str, test_id: str) -> str: """Update QA doc image references for Confluence.""" content = Path(qa_doc_path).read_text() # Pattern: ./screenshots/{test-id}/filename.png # Replace with: {test-id}-filename.png content = re.sub( rf'\./screenshots/{test_id}/([^)]+\.png)', rf'{test_id}-\1', content ) # Pattern: ./screenshots/{test-id}/elements/filename.png # Replace with: {test-id}-elem-filename.png content = re.sub( rf'{test_id}-elements/([^)]+\.png)', rf'{test_id}-elem-\1', content ) return content
Fixing Broken Image References in Existing Pages
If a page was created with
ri:url instead of ri:attachment, images won't display. To fix:
#!/bin/bash # fix-confluence-image-refs.sh - Fix broken image references in Confluence page PAGE_ID="$1" # 1. Get current page content curl -s -u "${ATLASSIAN_EMAIL}:${ATLASSIAN_API_TOKEN}" \ "https://${ATLASSIAN_DOMAIN}.atlassian.net/wiki/rest/api/content/${PAGE_ID}?expand=body.storage,version" \ > /tmp/page.json VERSION=$(jq '.version.number' /tmp/page.json) TITLE=$(jq -r '.title' /tmp/page.json) # 2. Extract and fix the content jq -r '.body.storage.value' /tmp/page.json > /tmp/content.html # Fix: Replace ri:url with ri:attachment for local filenames (not full URLs) # This converts: <ri:url ri:value="filename.png" /> # To: <ri:attachment ri:filename="filename.png" /> sed -i '' 's/<ri:url ri:value="\([^"]*\)" \/>/<ri:attachment ri:filename="\1" \/>/g' /tmp/content.html # Remove ac:src attribute (not needed with ri:attachment) sed -i '' 's/ ac:src="[^"]*"//g' /tmp/content.html # 3. Update the page NEW_VERSION=$((VERSION + 1)) CONTENT=$(cat /tmp/content.html | jq -Rs .) curl -s -u "${ATLASSIAN_EMAIL}:${ATLASSIAN_API_TOKEN}" \ -X PUT \ -H "Content-Type: application/json" \ "https://${ATLASSIAN_DOMAIN}.atlassian.net/wiki/rest/api/content/${PAGE_ID}" \ -d "{ \"version\": {\"number\": ${NEW_VERSION}}, \"title\": \"${TITLE}\", \"type\": \"page\", \"body\": { \"storage\": { \"value\": ${CONTENT}, \"representation\": \"storage\" } } }" echo "Fixed image references in page ${PAGE_ID}"
Common symptoms of broken image references:
- Images show as broken/missing icons
- Images display with blob URLs like
blob:https://media.staging.atl-paas.net/... - Alt text shows but image doesn't load
Jira Attachment Commands
Add Attachment
Action: Add attachment to Jira issue Issue: PROJ-123 File: /path/to/screenshot.png Expected behavior: - File uploaded to issue attachments - Visible in Attachments section - Downloadable by team members
List Attachments
Action: List all attachments on PROJ-123 Response format: - screenshot.png (234 KB) - Added by John on 2025-01-05 - error-log.txt (12 KB) - Added by Jane on 2025-01-04
Delete Attachment
Action: Remove old-screenshot.png from PROJ-123 Note: Requires appropriate permissions
Confluence Attachment Commands
Add to Page
Action: Attach file to Confluence page Space: TEAM Page: "Sprint Review" File: /path/to/presentation.pdf
Embed in Content
Action: Embed image in page content Space: TEAM Page: "Architecture Overview" File: system-diagram.png Position: After "System Components" heading Width: 800px
Replace Attachment
Action: Update existing attachment Space: TEAM Page: "API Docs" File: api-spec-v2.pdf Replace: api-spec-v1.pdf
Integration with QA Workflows
Attach QA Test Evidence
When a QA test is executed:
-
Capture screenshots during test
qa-tests/screenshots/QA-20250105-001/ ├── 01-initial-state.png ├── 02-form-filled.png └── 03-error-state.png -
Create/update Jira issue
Create bug: "Login form validation not working" Project: QA -
Attach evidence
Attach all screenshots from qa-tests/screenshots/QA-20250105-001/ to the created issue -
Add summary comment
"Test execution evidence attached: - [^01-initial-state.png] - Before test - [^02-form-filled.png] - Form with test data - [^03-error-state.png] - Error encountered"
Sync QA Documentation to Confluence
Action: Upload QA test procedure to Confluence Steps: 1. Convert QA markdown to Confluence format 2. Create/update page in QA space 3. Attach all element screenshots 4. Embed screenshots in page content
Batch Operations
Upload Directory Contents
Prompt: "Upload all files from ./release-assets/ to the 'v2.0 Release' Confluence page" Behavior: - Scan directory for supported files - Upload each file as attachment - Report progress and results
Sync Screenshots to Issue
Prompt: "Sync screenshots from ./qa-tests/screenshots/PROJ-123/ to Jira issue PROJ-123" Behavior: - Compare local files with existing attachments - Upload new files - Optionally replace updated files - Skip unchanged files
Error Handling
Common Errors
| Error | Cause | Resolution |
|---|---|---|
| File too large | Exceeds size limit | Compress or split file |
| Unsupported type | Extension not allowed | Convert to supported format |
| Permission denied | No attach permission | Request project/space access |
| Issue not found | Invalid issue key | Verify issue exists |
| Page not found | Invalid page title/space | Check space key and page title |
Handling Large Files
If file > max size: 1. For images: Compress or resize 2. For documents: Split into parts 3. For archives: Use cloud storage link instead Alternative: Upload to cloud storage and link in description
Best Practices
Organization
- Use consistent naming - Follow naming conventions above
- Group related files - Attach all evidence for one issue together
- Add descriptions - Include context in comments
- Clean up old attachments - Remove outdated files
Performance
- Compress images before upload (PNG → optimized PNG or JPEG)
- Batch uploads when attaching multiple files
- Check existing attachments before uploading duplicates
Security
- Redact sensitive data from screenshots
- Check file contents before uploading logs
- Use appropriate spaces/projects for confidential docs
Confluence Macros for Attachments
Image Display
!filename.png! # Basic !filename.png|width=600! # With width !filename.png|thumbnail! # As thumbnail
File Links
[^filename.pdf] # Download link [View Document^filename.pdf] # Custom link text
Gallery View
{gallery:include=*.png} # All PNG attachments {gallery:include=screenshot-*} # Matching pattern
PDF Viewer
{viewfile:filename.pdf} # Inline PDF viewer {viewfile:filename.pdf|height=600}