confluence-api-doc

Installation
SKILL.md

Confluence API Doc Sync

Sync API documentation from a multi-file docs/api/ directory to Confluence — one endpoint file = one Confluence page. The directory structure maps directly to a Confluence page tree, so no heading-based splitting is needed.

Uses acli for authentication verification and page reading (to get current version), and Confluence REST API via curl for page updates.

Core Principle: Directory Structure = Confluence Page Tree

The docs/api/ directory (generated by api-doc-gen) already organizes endpoints as individual files grouped by domain. This skill maps that structure directly to Confluence:

docs/api/                              Confluence Page Tree
├── index.md                     →     Parent Page (overview + common errors)
├── consent/                     →     ├── Consent (domain group page)
│   ├── accept-consent.md        →     │   ├── Accept Consent
│   ├── get-consent.md           →     │   ├── Get Consent
│   └── revoke-consent.md        →     │   └── Revoke Consent
├── channel/                     →     ├── Channel (domain group page)
│   ├── create-channel.md        →     │   ├── Create Channel
│   └── get-all-channels.md      →     │   └── Get All Channels
└── purpose/                     →     └── Purpose (domain group page)
    ├── create-purpose.md        →         ├── Create Purpose
    └── get-purposes.md          →         └── Get Purposes

Each .md endpoint file becomes exactly one Confluence page. Group directories become parent pages.

Step 1: Gather Required Information

Ask the user for the following (if not already provided):

  1. API doc directory path — e.g., docs/api/ (relative to project root, or absolute). Default: docs/api/
  2. Parent page URL — the Confluence page/folder under which API doc pages live (or will be created), e.g., https://company.atlassian.net/wiki/spaces/PROJ/pages/123456789/API+Reference or a folder URL like https://company.atlassian.net/wiki/spaces/PROJ/folder/123456789

Extract the page ID directly from the URL (the numeric segment, e.g., 123456789).

Validate the directory:

  • index.md must exist in the provided path
  • At least one subdirectory with .md files must exist

Do NOT ask for Confluence URL, email, or API token — those are resolved automatically in the next step.

Step 2: Verify Authentication and Resolve Credentials

acli auth status

If not authenticated or acli not found:

From the output, extract:

  • CONFLUENCE_URLSite: field prefixed with https://, e.g., company.atlassian.nethttps://company.atlassian.net
  • EMAILEmail: field, e.g., user@company.com

Why REST API for writes? acli confluence page currently only supports view. For page create/update we use Confluence REST API via curl. URL and email come from acli auth status; only the API token needs to be resolved (at write time).

Step 3: Read Directory Structure

Scan the docs/api/ directory to build the page list. No heading-based parsing needed — the file/directory structure is the source of truth.

Scanning Steps

  1. Read index.md — extract service name (from H1), overview paragraph, and Common Error Responses section
  2. List subdirectories — each subdirectory = one domain group (e.g., consent/ → "Consent"). Skip health/ — health check endpoints are infrastructure-only and not synced to Confluence.
  3. List .md files per subdirectory — each file = one API endpoint page
  4. Extract page title per endpoint — read the Method and Path fields from the file, then format as METHOD: /path (e.g., POST: /api/v1/consents). This is the Confluence page title — not the H1 heading.
  5. Extract page content per endpoint — use the full file content, but:
    • Strip the breadcrumb line (first line starting with >)
    • Strip the H1 heading (used as in-page heading, not page title)

Page Types

Source Page Type Title Content
Group directory Domain group Directory name → Title Case (e.g., consent → "Consent") Brief intro or empty
Endpoint .md file API page METHOD: /path from Method + Path fields (e.g., POST: /api/v1/consents) File content (minus breadcrumb)
index.md overview Parent page content Service name from H1 Overview + Common Errors (appended to parent page)

Summary Output

After scanning, show the user a structured summary:

Found N domain groups, M individual API endpoints:

  Consent (5 APIs)
     POST: /api/v1/consents
     GET: /api/v1/consents/:citizen_id
     GET: /api/v1/consents/:id
     GET: /api/v1/consents/:id/history
     DELETE: /api/v1/consents/:id/revoke
  Channel (5 APIs)
     POST: /api/v1/channels
     GET: /api/v1/channels
     ...
  Purpose (11 APIs)
     POST: /api/v1/purposes
     GET: /api/v1/purposes
     ...

Total pages to create/update: N (domain groups) + M (APIs) = T pages

Ask the user to confirm or specify which sections to sync (all, specific domains, or specific APIs).

Step 4: Map Sections to Confluence Page Hierarchy

The page structure in Confluence mirrors the directory structure:

Parent page (provided by user)
├── Domain Group pages (directories)   ← first-level children
│   └── Individual API pages (files)   ← second-level children

Discover Existing Pages

First, fetch all children (and grandchildren) under the parent page to find existing pages:

# Get direct children of parent page
curl -s "${CONFLUENCE_URL}/wiki/rest/api/content/${PARENT_PAGE_ID}?expand=space,children.page" \
  -u "${EMAIL}:${API_TOKEN}"

From the response extract:

  • space.key → save as SPACE_KEY (needed for creating new pages)
  • children.page.results[] → list of {id, title} for direct children

For each direct child that looks like a domain group, also fetch its children:

curl -s "${CONFLUENCE_URL}/wiki/rest/api/content/${DOMAIN_GROUP_PAGE_ID}/child/page" \
  -u "${EMAIL}:${API_TOKEN}"

Build the Mapping

Match existing page titles against scanned sections to build the mapping:

Source Page Title Type Matched Page ID Action
consent/ Consent Domain group 456789 Update
consent/accept-consent.md POST: /api/v1/consents API page 567890 Update
consent/revoke-consent.md DELETE: /api/v1/consents/:id/revoke API page Create (under 456789)
purpose/ Purpose Domain group Create (under parent)
purpose/create-purpose.md POST: /api/v1/purposes API page Create (under new domain group page)

Important ordering: When creating new pages, domain group pages must be created before their child API pages (because child pages need the parent's page ID as ancestor).

Matching Strategy

  • Match by exact page title (e.g., POST: /api/v1/consents matches existing page with same title)
  • If ambiguous, show the user and ask them to confirm the mapping
  • For unmatched sections → mark as "Create new"

Step 5: Get Current Page Versions

For each page in the mapping, fetch its current version number (required for updates):

acli confluence page view --id <PAGE_ID> --include-version --json

Extract version.number from the JSON output. Store as CURRENT_VERSION per page.

Step 6: Convert Markdown to Confluence Storage Format

For each endpoint file, convert the Markdown content to Confluence storage format (XHTML-based).

Pre-processing

Before conversion, strip from each endpoint file:

  1. Breadcrumb line — first line starting with > (e.g., > [API Documentation](../index.md) > ...)
  2. H1 heading — first # line (used as page title, not body content)

Conversion Rules

Markdown Confluence Storage
```lang\ncode\n``` <ac:structured-macro ac:name="code"><ac:parameter ac:name="language">lang</ac:parameter><ac:plain-text-body><![CDATA[code]]></ac:plain-text-body></ac:structured-macro>
**bold** <strong>bold</strong>
*italic* <em>italic</em>
[text](url) <a href="url">text</a>
## Heading <h2>Heading</h2>
| col | col | table <table><tbody><tr><td>...</td></tr></tbody></table>

For js, javascript, sh, bash, json, yaml code blocks, map to the Confluence language name accordingly (bash for sh, javascript for js).

Step 7: Sync Pages (Create + Update) via REST API

Before writing, resolve the API token:

echo $CONFLUENCE_API_TOKEN
  • If set → use it silently, no need to ask
  • If empty → ask the user once:

    "ต้องการ API token สำหรับ write ผ่าน Confluence REST API (acli ยังไม่ support page write) — generate ได้ที่ https://id.atlassian.com/manage-profile/security/api-tokens"

Use EMAIL extracted from acli auth status in Step 2.

Execution Order (critical for parent-child hierarchy)

  1. First pass — Domain group pages: Create or update all domain group pages as children of the parent page. This ensures parent page IDs exist before creating child API pages.
  2. Second pass — Individual API pages: Create or update all endpoint pages as children of their respective domain group pages.

Creating a New Page

curl -s -X POST \
  "${CONFLUENCE_URL}/wiki/rest/api/content" \
  -u "${EMAIL}:${API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "{
    \"type\": \"page\",
    \"title\": \"${PAGE_TITLE}\",
    \"ancestors\": [{\"id\": \"${ANCESTOR_PAGE_ID}\"}],
    \"space\": {\"key\": \"${SPACE_KEY}\"},
    \"body\": {
      \"storage\": {
        \"value\": \"${ESCAPED_HTML}\",
        \"representation\": \"storage\"
      }
    }
  }"
  • For domain group pages: ANCESTOR_PAGE_ID = user-provided parent page ID
  • For individual API pages: ANCESTOR_PAGE_ID = the domain group page ID (created in first pass)

Extract id from the response to use as ancestor for child pages.

Updating an Existing Page

curl -s -X PUT \
  "${CONFLUENCE_URL}/wiki/rest/api/content/${PAGE_ID}" \
  -u "${EMAIL}:${API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "{
    \"version\": {\"number\": $((CURRENT_VERSION + 1))},
    \"title\": \"${PAGE_TITLE}\",
    \"type\": \"page\",
    \"body\": {
      \"storage\": {
        \"value\": \"${ESCAPED_HTML}\",
        \"representation\": \"storage\"
      }
    }
  }"

Check HTTP status — 200 means success.

Content comparison tip: Before updating, compare normalized content (collapse whitespace) to skip pages with no real changes. This avoids unnecessary version bumps.

Step 8: Report Results

Print a summary table after all operations:

| Page Title                              | Type           | Page ID    | Status                  |
|-----------------------------------------|----------------|------------|-------------------------|
| Consent                                 | Domain group   | 456789     | Updated (v3 → v4)       |
| POST: /api/v1/consents                  | API page       | 567890     | Updated (v2 → v3)       |
| DELETE: /api/v1/consents/:id/revoke     | API page       | 678901     | Created                 |
| GET: /api/v1/consents/:citizen_id       | API page       | 567890     | Skipped (no changes)    |
| Purpose                                 | Domain group   | 789012     | Created                 |
| POST: /api/v1/purposes                  | API page       | 890123     | Created                 |

Total: N domain groups, M API pages updated, K created, J skipped, F failed.

Error Reference

Scenario Action
acli not found Install via brew install atlassian/tap/acli
acli auth status fails Run acli auth login
API doc directory not found or missing index.md Re-ask for correct directory path
HTTP 401 on REST call Check API token — re-check $CONFLUENCE_API_TOKEN or ask user
HTTP 404 on page Verify page ID is correct; page may have been deleted
HTTP 409 version conflict Re-fetch version with acli and retry
Section title has no match Ask user to manually provide page ID
Related skills
Installs
27
Repository
witooh/skills
First Seen
Mar 11, 2026