🎁 Get the FREE AI Skills Starter Guide β€” Subscribe β†’
BytesAgainBytesAgain
πŸ¦€ ClawHub

Confluence

by @byungkyu

Confluence API integration with managed OAuth. Manage pages, spaces, blogposts, comments, and attachments. Use this skill when users want to create, read, up...

TERMINAL
clawhub install confluence-api

πŸ“– About This Skill


name: confluence description: | Confluence API integration with managed OAuth. Manage pages, spaces, blogposts, comments, and attachments. Use this skill when users want to create, read, update, or delete Confluence content, manage spaces, or work with comments and attachments. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway). Requires network access and valid Maton API key. metadata: author: maton version: "1.0" clawdbot: emoji: 🧠 homepage: "https://maton.ai" requires: env: - MATON_API_KEY

Confluence

Access the Confluence Cloud API with managed OAuth authentication. Manage pages, spaces, blogposts, comments, attachments, and properties.

Quick Start

# List pages in your Confluence site
python3 <<'EOF'
import urllib.request, os, json

First get your Cloud ID

req = urllib.request.Request('https://api.maton.ai/confluence/oauth/token/accessible-resources') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') resources = json.load(urllib.request.urlopen(req)) cloud_id = resources[0]['id']

Then list pages

req = urllib.request.Request(f'https://api.maton.ai/confluence/ex/confluence/{cloud_id}/wiki/api/v2/pages') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF

Base URL

https://api.maton.ai/confluence/{atlassian-api-path}

Confluence Cloud uses two URL patterns:

V2 API (recommended):

https://api.maton.ai/confluence/ex/confluence/{cloudId}/wiki/api/v2/{resource}

V1 REST API (limited):

https://api.maton.ai/confluence/ex/confluence/{cloudId}/wiki/rest/api/{resource}

The {cloudId} is required for all API calls. Obtain it via the accessible-resources endpoint (see below).

Authentication

All requests require the Maton API key in the Authorization header:

Authorization: Bearer $MATON_API_KEY

Environment Variable: Set your API key as MATON_API_KEY:

export MATON_API_KEY="YOUR_API_KEY"

Getting Your API Key

1. Sign in or create an account at maton.ai 2. Go to maton.ai/settings 3. Copy your API key

Connection Management

Manage your Confluence OAuth connections at https://api.maton.ai.

List Connections

python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections?app=confluence&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Create Connection

python3 <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'confluence'}).encode()
req = urllib.request.Request('https://api.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Get Connection

python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Response:

{
  "connection": {
    "connection_id": "{connection_id}",
    "status": "ACTIVE",
    "creation_time": "2026-02-13T00:00:00.000000Z",
    "last_updated_time": "2026-02-13T00:00:00.000000Z",
    "url": "https://connect.maton.ai/?session_token=...",
    "app": "confluence",
    "metadata": {}
  }
}

Open the returned url in a browser to complete OAuth authorization.

Delete Connection

python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Specifying Connection

If you have multiple Confluence connections, specify which one to use with the Maton-Connection header:

python3 <<'EOF'
import urllib.request, os, json
cloud_id = "YOUR_CLOUD_ID"
req = urllib.request.Request(f'https://api.maton.ai/confluence/ex/confluence/{cloud_id}/wiki/api/v2/pages')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '{connection_id}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

If you have multiple connections, always include this header to ensure requests go to the intended account.

Getting Your Cloud ID

Before making API calls, you must obtain your Confluence Cloud ID:

python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/confluence/oauth/token/accessible-resources')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
resources = json.load(urllib.request.urlopen(req))
print(json.dumps(resources, indent=2))

Use resources[0]['id'] as your cloudId

EOF

Response:

[
  {
    "id": "62909843-b784-4c35-b770-e4e2a26f024b",
    "name": "your-site-name",
    "url": "https://your-site.atlassian.net",
    "scopes": ["read:confluence-content.all", "write:confluence-content", ...],
    "avatarUrl": "https://..."
  }
]

Security & Permissions

  • Access is scoped to pages, spaces, blogposts, comments, and attachments within the connected Confluence account.
  • All write operations require explicit user approval. Before executing any create, update, or delete call, confirm the target resource and intended effect with the user.
  • API Reference

    All V2 API endpoints use the base path:

    /confluence/ex/confluence/{cloudId}/wiki/api/v2
    

    Pages

    #### List Pages

    GET /pages
    GET /pages?space-id={spaceId}
    GET /pages?limit=25
    GET /pages?status=current
    GET /pages?body-format=storage
    

    Response:

    {
      "results": [
        {
          "id": "98391",
          "status": "current",
          "title": "My Page",
          "spaceId": "98306",
          "parentId": "98305",
          "parentType": "page",
          "authorId": "557058:...",
          "createdAt": "2026-02-12T23:00:00.000Z",
          "version": {
            "number": 1,
            "authorId": "557058:...",
            "createdAt": "2026-02-12T23:00:00.000Z"
          },
          "_links": {
            "webui": "/spaces/SPACEKEY/pages/98391/My+Page"
          }
        }
      ],
      "_links": {
        "next": "/wiki/api/v2/pages?cursor=..."
      }
    }
    

    #### Get Page

    GET /pages/{pageId}
    GET /pages/{pageId}?body-format=storage
    GET /pages/{pageId}?body-format=atlas_doc_format
    GET /pages/{pageId}?body-format=view
    

    Body formats:

  • storage - Confluence storage format (XML-like)
  • atlas_doc_format - Atlassian Document Format (JSON)
  • view - Rendered HTML
  • #### Create Page

    POST /pages
    Content-Type: application/json

    { "spaceId": "98306", "status": "current", "title": "New Page Title", "body": { "representation": "storage", "value": "

    Page content in storage format

    " } }

    To create a child page, include parentId:

    {
      "spaceId": "98306",
      "parentId": "98391",
      "status": "current",
      "title": "Child Page",
      "body": {
        "representation": "storage",
        "value": "

    Child page content

    " } }

    Response:

    {
      "id": "98642",
      "status": "current",
      "title": "New Page Title",
      "spaceId": "98306",
      "version": {
        "number": 1
      }
    }
    

    #### Update Page

    PUT /pages/{pageId}
    Content-Type: application/json

    { "id": "98391", "status": "current", "title": "Updated Page Title", "body": { "representation": "storage", "value": "

    Updated content

    " }, "version": { "number": 2, "message": "Updated via API" } }

    Note: You must increment the version number with each update.

    #### Delete Page

    DELETE /pages/{pageId}
    

    Returns 204 No Content on success.

    #### Get Page Children

    GET /pages/{pageId}/children
    

    #### Get Page Versions

    GET /pages/{pageId}/versions
    

    #### Get Page Labels

    GET /pages/{pageId}/labels
    

    #### Get Page Attachments

    GET /pages/{pageId}/attachments
    

    #### Get Page Comments

    GET /pages/{pageId}/footer-comments
    

    #### Get Page Properties

    GET /pages/{pageId}/properties
    GET /pages/{pageId}/properties/{propertyId}
    

    #### Create Page Property

    POST /pages/{pageId}/properties
    Content-Type: application/json

    { "key": "my-property-key", "value": {"customKey": "customValue"} }

    #### Update Page Property

    PUT /pages/{pageId}/properties/{propertyId}
    Content-Type: application/json

    { "key": "my-property-key", "value": {"customKey": "updatedValue"}, "version": {"number": 2} }

    #### Delete Page Property

    DELETE /pages/{pageId}/properties/{propertyId}
    

    Spaces

    #### List Spaces

    GET /spaces
    GET /spaces?limit=25
    GET /spaces?type=global
    

    Response:

    {
      "results": [
        {
          "id": "98306",
          "key": "SPACEKEY",
          "name": "Space Name",
          "type": "global",
          "status": "current",
          "authorId": "557058:...",
          "createdAt": "2026-02-12T23:00:00.000Z",
          "homepageId": "98305",
          "_links": {
            "webui": "/spaces/SPACEKEY"
          }
        }
      ]
    }
    

    #### Get Space

    GET /spaces/{spaceId}
    

    #### Get Space Pages

    GET /spaces/{spaceId}/pages
    

    #### Get Space Blogposts

    GET /spaces/{spaceId}/blogposts
    

    #### Get Space Properties

    GET /spaces/{spaceId}/properties
    

    #### Create Space Property

    POST /spaces/{spaceId}/properties
    Content-Type: application/json

    { "key": "space-property-key", "value": {"key": "value"} }

    #### Get Space Permissions

    GET /spaces/{spaceId}/permissions
    

    #### Get Space Labels

    GET /spaces/{spaceId}/labels
    

    Blogposts

    #### List Blogposts

    GET /blogposts
    GET /blogposts?space-id={spaceId}
    GET /blogposts?limit=25
    

    #### Get Blogpost

    GET /blogposts/{blogpostId}
    GET /blogposts/{blogpostId}?body-format=storage
    

    #### Create Blogpost

    POST /blogposts
    Content-Type: application/json

    { "spaceId": "98306", "title": "My Blog Post", "body": { "representation": "storage", "value": "

    Blog post content

    " } }

    #### Update Blogpost

    PUT /blogposts/{blogpostId}
    Content-Type: application/json

    { "id": "458753", "status": "current", "title": "Updated Blog Post", "body": { "representation": "storage", "value": "

    Updated content

    " }, "version": { "number": 2 } }

    #### Delete Blogpost

    DELETE /blogposts/{blogpostId}
    

    #### Get Blogpost Labels

    GET /blogposts/{blogpostId}/labels
    

    #### Get Blogpost Versions

    GET /blogposts/{blogpostId}/versions
    

    #### Get Blogpost Comments

    GET /blogposts/{blogpostId}/footer-comments
    

    Comments

    #### List Footer Comments

    GET /footer-comments
    GET /footer-comments?body-format=storage
    

    #### Get Comment

    GET /footer-comments/{commentId}
    

    #### Create Footer Comment

    POST /footer-comments
    Content-Type: application/json

    { "pageId": "98391", "body": { "representation": "storage", "value": "

    Comment text

    " } }

    For blogpost comments:

    {
      "blogpostId": "458753",
      "body": {
        "representation": "storage",
        "value": "

    Comment on blogpost

    " } }

    #### Update Comment

    PUT /footer-comments/{commentId}
    Content-Type: application/json

    { "version": {"number": 2}, "body": { "representation": "storage", "value": "

    Updated comment

    " } }

    #### Delete Comment

    DELETE /footer-comments/{commentId}
    

    #### Get Comment Replies

    GET /footer-comments/{commentId}/children
    

    #### List Inline Comments

    GET /inline-comments
    

    Attachments

    #### List Attachments

    GET /attachments
    GET /attachments?limit=25
    

    #### Get Attachment

    GET /attachments/{attachmentId}
    

    #### Get Page Attachments

    GET /pages/{pageId}/attachments
    

    Tasks

    #### List Tasks

    GET /tasks
    

    #### Get Task

    GET /tasks/{taskId}
    

    Labels

    #### List Labels

    GET /labels
    GET /labels?prefix=global
    

    Custom Content

    #### List Custom Content

    GET /custom-content
    GET /custom-content?type={customContentType}
    

    User (V1 API)

    The current user endpoint uses the V1 REST API:

    GET /confluence/ex/confluence/{cloudId}/wiki/rest/api/user/current
    

    Response:

    {
      "type": "known",
      "accountId": "557058:...",
      "accountType": "atlassian",
      "email": "user@example.com",
      "publicName": "User Name",
      "displayName": "User Name"
    }
    

    Pagination

    The V2 API uses cursor-based pagination. Responses include a _links.next URL when more results are available.

    GET /pages?limit=25
    

    Response:

    {
      "results": [...],
      "_links": {
        "next": "/wiki/api/v2/pages?cursor=eyJpZCI6Ijk4MzkyIn0"
      }
    }
    

    To get the next page, extract the cursor and pass it:

    GET /pages?limit=25&cursor=eyJpZCI6Ijk4MzkyIn0
    

    Code Examples

    JavaScript

    // Get Cloud ID first
    const resourcesRes = await fetch(
      'https://api.maton.ai/confluence/oauth/token/accessible-resources',
      {
        headers: {
          'Authorization': Bearer ${process.env.MATON_API_KEY}
        }
      }
    );
    const resources = await resourcesRes.json();
    const cloudId = resources[0].id;

    // List pages const response = await fetch( https://api.maton.ai/confluence/ex/confluence/${cloudId}/wiki/api/v2/pages, { headers: { 'Authorization': Bearer ${process.env.MATON_API_KEY} } } ); const data = await response.json();

    Python

    import os
    import requests

    Get Cloud ID first

    resources = requests.get( 'https://api.maton.ai/confluence/oauth/token/accessible-resources', headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'} ).json() cloud_id = resources[0]['id']

    List pages

    response = requests.get( f'https://api.maton.ai/confluence/ex/confluence/{cloud_id}/wiki/api/v2/pages', headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'} ) data = response.json()

    Notes

  • Cloud ID Required: You must obtain your Cloud ID via /oauth/token/accessible-resources before making API calls
  • V2 API Recommended: Use the V2 API (/wiki/api/v2/) for most operations. The V1 API (/wiki/rest/api/) is limited
  • Body Formats: Use storage format for creating/updating content. Use view for rendered HTML
  • Version Numbers: When updating pages or blogposts, you must increment the version number
  • Storage Format: Content uses Confluence storage format (XML-like). Example:

    Paragraph

    ,

    Heading

  • Delete Returns 204: DELETE operations return 204 No Content with no response body
  • IDs are Strings: Page, space, and other IDs should be passed as strings
  • IMPORTANT: When piping curl output to jq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments
  • Error Handling

    | Status | Meaning | |--------|---------| | 400 | Bad request or malformed data | | 401 | Invalid API key or insufficient OAuth scopes | | 403 | Permission denied | | 404 | Resource not found | | 409 | Conflict (e.g., duplicate title) | | 429 | Rate limited | | 4xx/5xx | Passthrough error from Confluence API |

    Troubleshooting: API Key Issues

    1. Check that the MATON_API_KEY environment variable is set:

    echo $MATON_API_KEY
    

    2. Verify the API key is valid by listing connections:

    python3 <<'EOF'
    import urllib.request, os, json
    req = urllib.request.Request('https://api.maton.ai/connections')
    req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
    print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
    EOF
    

    Troubleshooting: Invalid App Name

    Ensure your URL path starts with confluence. For example:

  • Correct: https://api.maton.ai/confluence/ex/confluence/{cloudId}/wiki/api/v2/pages
  • Incorrect: https://api.maton.ai/ex/confluence/{cloudId}/wiki/api/v2/pages
  • Troubleshooting: Scope Issues

    If you receive a 401 error with "scope does not match", you may need to re-authorize with the required scopes. Delete your connection and create a new one:

    # Delete existing connection
    python3 <<'EOF'
    import urllib.request, os, json
    req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}', method='DELETE')
    req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
    print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
    EOF

    Create new connection

    python3 <<'EOF' import urllib.request, os, json data = json.dumps({'app': 'confluence'}).encode() req = urllib.request.Request('https://api.maton.ai/connections', data=data, method='POST') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('Content-Type', 'application/json') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF

    Resources

  • Confluence REST API V2 Documentation
  • Confluence REST API V2 Reference
  • Confluence Storage Format
  • Maton Community
  • Maton Support
  • πŸ’‘ Examples

    # List pages in your Confluence site
    python3 <<'EOF'
    import urllib.request, os, json

    First get your Cloud ID

    req = urllib.request.Request('https://api.maton.ai/confluence/oauth/token/accessible-resources') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') resources = json.load(urllib.request.urlopen(req)) cloud_id = resources[0]['id']

    Then list pages

    req = urllib.request.Request(f'https://api.maton.ai/confluence/ex/confluence/{cloud_id}/wiki/api/v2/pages') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF

    πŸ“‹ Tips & Best Practices

  • Cloud ID Required: You must obtain your Cloud ID via /oauth/token/accessible-resources before making API calls
  • V2 API Recommended: Use the V2 API (/wiki/api/v2/) for most operations. The V1 API (/wiki/rest/api/) is limited
  • Body Formats: Use storage format for creating/updating content. Use view for rendered HTML
  • Version Numbers: When updating pages or blogposts, you must increment the version number
  • Storage Format: Content uses Confluence storage format (XML-like). Example:

    Paragraph

    ,

    Heading

  • Delete Returns 204: DELETE operations return 204 No Content with no response body
  • IDs are Strings: Page, space, and other IDs should be passed as strings
  • IMPORTANT: When piping curl output to jq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments