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://gateway.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://gateway.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://gateway.maton.ai/confluence/{atlassian-api-path}
Confluence Cloud uses two URL patterns:
V2 API (recommended):
https://gateway.maton.ai/confluence/ex/confluence/{cloudId}/wiki/api/v2/{resource}
V1 REST API (limited):
https://gateway.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
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- Copy your API key
Connection Management
Manage your Confluence OAuth connections at https://ctrl.maton.ai.
List Connections
python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.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://ctrl.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://ctrl.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": "6cb7787f-7c32-4658-a3c3-4ddf1367a4ce",
"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://ctrl.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://gateway.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', '6cb7787f-7c32-4658-a3c3-4ddf1367a4ce')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If omitted, the gateway uses the default (oldest) active connection.
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://gateway.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://..."
}
]
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": "<p>Page content in storage format</p>"
}
}
To create a child page, include parentId:
{
"spaceId": "98306",
"parentId": "98391",
"status": "current",
"title": "Child Page",
"body": {
"representation": "storage",
"value": "<p>Child page content</p>"
}
}
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": "<p>Updated content</p>"
},
"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": "<p>Blog post content</p>"
}
}
Update Blogpost
PUT /blogposts/{blogpostId}
Content-Type: application/json
{
"id": "458753",
"status": "current",
"title": "Updated Blog Post",
"body": {
"representation": "storage",
"value": "<p>Updated content</p>"
},
"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": "<p>Comment text</p>"
}
}
For blogpost comments:
{
"blogpostId": "458753",
"body": {
"representation": "storage",
"value": "<p>Comment on blogpost</p>"
}
}
Update Comment
PUT /footer-comments/{commentId}
Content-Type: application/json
{
"version": {"number": 2},
"body": {
"representation": "storage",
"value": "<p>Updated comment</p>"
}
}
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://gateway.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://gateway.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://gateway.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://gateway.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-resourcesbefore 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
storageformat for creating/updating content. Useviewfor 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:
<p>Paragraph</p>,<h1>Heading</h1> - 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
jqor other commands, environment variables like$MATON_API_KEYmay 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
- Check that the
MATON_API_KEYenvironment variable is set:
echo $MATON_API_KEY
- Verify the API key is valid by listing connections:
python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.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://gateway.maton.ai/confluence/ex/confluence/{cloudId}/wiki/api/v2/pages - Incorrect:
https://gateway.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://ctrl.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://ctrl.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