| name | tracking-confluence |
| description | Confluence page management, space administration, and content collaboration. Use when working with Confluence pages, spaces, comments, or Atlassian connections. |
| connection_type | atlassian |
| preload | false |
Tracking Confluence
Discovery
**If no `[cached_from_skill:tracking-confluence:discover]` context exists, run discovery first:**
```bash
bun run ./_skills/connections/confluence/tracking-confluence/scripts/discover.ts
bun run ./_skills/connections/confluence/tracking-confluence/scripts/discover.ts --max-spaces 25
```
Output is auto-cached.
What discovery provides:
resources: List of accessible Atlassian sites with cloudId, url, name
currentUser: Your accountId, name, and email (for CQL queries like creator = currentUser())
spaces: Available spaces with id, key, name, type (global/personal); includes byType counts
recentPages: Last 10 modified pages with title, spaceKey, type, modified
hints: Pagination model info (cursor-based via _links.next)
Why run discovery:
- Get
cloudId required for all Confluence API calls
- Know available spaces before creating pages
- Get your
accountId for CQL queries
- Understand pagination model (cursor-based, not offset-based)
Tools
Pages: getConfluencePage(cloudId, pageId, expand?), createConfluencePage(cloudId, spaceId, title, body, contentFormat?), updateConfluencePage(cloudId, pageId, title, body, contentFormat?, versionMessage?), getConfluencePageDescendants(cloudId, pageId)
Spaces: getConfluenceSpaces(cloudId, limit?), getPagesInConfluenceSpace(cloudId, spaceId)
Comments: getConfluencePageFooterComments(cloudId, pageId), getConfluencePageInlineComments(cloudId, pageId), createConfluenceFooterComment(cloudId, pageId, body)
Search: searchConfluenceUsingCql(cloudId, cql, maxResults?)
Quick Patterns
Get cloudId (always first):
const resources = await getAccessibleAtlassianResources();
const cloudId = resources[0].id;
const siteUrl = resources[0].url;
Create page:
const spaces = await getConfluenceSpaces({ cloudId, limit: 10 });
const spaceId = spaces.results[0].id;
const result = await createConfluencePage({ cloudId, spaceId: spaceId.toString(), title: 'Page Title', body: '# Markdown', contentFormat: 'markdown' });
const url = `${siteUrl}/wiki/spaces/${spaceId}/pages/${result.id}`;
Search pages:
const results = await searchConfluenceUsingCql({ cloudId, cql: 'space = "DOCS" AND type = page AND title ~ "API"', maxResults: 10 });
results.results.forEach(page => console.log(page.title, page.url));
Update page:
const page = await getConfluencePage({ cloudId, pageId: '12345' });
await updateConfluencePage({ cloudId, pageId: '12345', title: 'Updated', body: '# New content', contentFormat: 'markdown' });
Pagination
**Confluence uses cursor-based pagination via `_links.next`.** The `start`, `limit`, and `size` fields may be `undefined` — do not rely on them for pagination logic.
Pattern:
let allResults = [];
let response = await getConfluenceSpaces({ cloudId, limit: 25 });
allResults.push(...response.results);
while (response._links?.next) {
response = await getConfluenceSpaces({ cloudId, limit: 25 });
allResults.push(...response.results);
}
Workflows
Create page: getAccessibleAtlassianResources() → getConfluenceSpaces({cloudId}) → createConfluencePage({cloudId, spaceId, title, body}) → return ${siteUrl}/wiki/spaces/${spaceId}/pages/${result.id}
Find pages: getAccessibleAtlassianResources() → searchConfluenceUsingCql({cloudId, cql: 'type = page AND title ~ "topic"'})
Update page: getConfluencePage({cloudId, pageId}) → updateConfluencePage({cloudId, pageId, title, body, contentFormat: 'markdown'})
CQL Reference
Operators: =, !=, ~ (contains), IN, >=, <=
Fields: space, type, title, text, created, modified, creator, contributor, label
**Note:** `status` field is NOT supported in CQL. Use `type` instead.
Examples: space = "DOCS" AND type = page, title ~ "API", created >= -7d ORDER BY created DESC, label = "important", creator = currentUser()
CQL Best Practices
**Always double-quote string values** to avoid reserved word collisions:
- `space = "IN"` — safe
- `space = IN` — CQL parse error
Reserved words: AND, OR, NOT, IN, IS, NULL, EMPTY, ORDER, BY, TO, FROM, etc.
Content Formats
| Format | contentFormat value | When to use | Notes |
|---|
| Markdown | markdown | Default for new pages | Simplest; auto-converted by Confluence |
| Storage | storage | Precise HTML control | Uses <ac:*> macros for rich content |
| Wiki | wiki | Legacy content | Rarely needed for new pages |
Recommendation: Use markdown as the default contentFormat. Only use storage when you need Confluence-specific macros (code blocks, panels, etc.).
SDK Utilities
Import from @connections/_utils (NOT from format module):
import { format, normalize, countBy, groupBy, parseArgs, parallel } from "@connections/_utils";
| Utility | Purpose |
|---|
format(data) | Pretty-print JSON output for discovery scripts |
normalize(arr, key) | Convert array to Record<string, T> keyed by field |
countBy(arr, fn) | Count items by category (e.g., spaces by type) |
groupBy(arr, fn) | Group items by category |
parseArgs() | Parse CLI flags (--max-spaces 25) |
parallel(items, fn, opts) | Execute async operations in parallel with fallback |
Defensive Coding
**Confluence API responses may omit fields that TypeScript types mark as present.** Always use optional chaining when accessing nested properties:
const webUrl = space.links.webui;
const webUrl = space.links?.webui ?? "N/A";
Fields commonly missing at runtime: space.links, space.homepage, page.version, result._links, start, limit, size.
Troubleshooting
| Error | Cause | Fix |
|---|
returned unexpected response: null | OAuth token expired | Re-authenticate |
CQL parse error | Unquoted reserved word | Double-quote string values |
start/limit/size undefined | Normal — pagination fields are optional | Use _links.next for pagination |
TypeError: undefined is not an object | Accessing nested property on missing field (e.g., space.links.webui) | Use optional chaining: space.links?.webui |
UNAUTHENTICATED | Token expired mid-session | Re-run discovery |
Page does not exist | Wrong cloudId or pageId | Verify cloudId from discovery |
| Discovery returns 0 spaces | Missing OAuth scopes | Ensure read:confluence-space.summary granted |
Connection Resilience
- Always use
cloudId from discovery output, never hardcode
- If discovery fails with auth errors, re-authenticate the Atlassian connection and re-run discovery
- For multi-site workspaces, discovery returns all accessible sites in
resources[] — pick the correct cloudId for the target site
Output Format
Present results as a structured report:
Tracking Confluence Report
══════════════════════════
Resources discovered: [count]
Resource Status Key Metric Issues
──────────────────────────────────────────────
[name] [ok/warn] [value] [findings]
Summary: [total] resources | [ok] healthy | [warn] warnings | [crit] critical
Action Items: [list of prioritized findings]
Target ≤50 lines of output. Use tables for multi-resource comparisons.
Anti-Hallucination Rules
- NEVER assume resource names — always discover via CLI/API in Phase 1 before referencing in Phase 2.
- NEVER fabricate metric names or dimensions — verify against the service documentation or
--help output.
- NEVER mix CLI commands between service versions — confirm which version/API you are targeting.
- ALWAYS use the discovery → verify → analyze chain — every resource referenced must have been discovered first.
- ALWAYS handle empty results gracefully — an empty response is valid data, not an error to retry.
Counter-Rationalizations
| Shortcut | Counter | Why |
|---|
| "I'll skip discovery and check known resources" | Always run Phase 1 discovery first | Resource names change, new resources appear — assumed names cause errors |
| "The user only asked for a quick check" | Follow the full discovery → analysis flow | Quick checks miss critical issues; structured analysis catches silent failures |
| "Default configuration is probably fine" | Audit configuration explicitly | Defaults often leave logging, security, and optimization features disabled |
| "Metrics aren't needed for this" | Always check relevant metrics when available | API/CLI responses show current state; metrics reveal trends and intermittent issues |
| "I don't have access to that" | Try the command and report the actual error | Assumed permission failures prevent useful investigation; actual errors are informative |