Skip to main content

Confluence AS

2.0.0: Confluence exposes a spec-driven api surface through as-engine. The reviewed 108-verb inventory retains 38 workflows, replaces 70 verbs with migration hints, and has no deferred implementations. jira create-from-page now creates Jira issues through the shared engine transport; its Confluence reads and page update use indexed operations, while the Jira transport remains the bounded JAS-51 exception. Start with confluence-as help, then read the migration notes and complete rename table before updating scripts.

PyPI version Python versions License

Python library for Confluence Cloud REST API - shared utilities for the Confluence Assistant Skills project.

CLI discovery (api describe, api search, help and --version) defers HTTP, legacy client and converter imports. Discovery no longer loads or validates scope configuration because it sends no requests; the same scope configuration is loaded once immediately before the first call, before any guard or transport runs. Command-group help such as help api still loads wrapper modules to enumerate their commands. Keep optional runtime imports at their point of use when extending these entry paths.

Features

  • ConfluenceClient - HTTP client with retry logic, pagination, and file uploads
  • ConfigManager - Multi-source configuration (env vars, JSON files)
  • Error Handling - Exception hierarchy and decorators for clean error handling
  • Validators - Input validation for page IDs, space keys, CQL queries, etc.
  • Formatters - Output formatting for pages, spaces, tables, JSON, CSV
  • ADF Helper - Atlassian Document Format conversion (Markdown ↔ ADF)
  • XHTML Helper - Legacy storage format conversion
  • Cache - File-based response caching with TTL

Installation

pip install confluence-as

Note: This is the library package. For the CLI tool, install confluence-assistant-skills instead:

pip install confluence-assistant-skills  # CLI with 'confluence' command

Quick Start

from confluence_as import (
    get_confluence_client,
    validate_page_id,
    format_page,
    markdown_to_adf,
)

# Set environment variables:
# CONFLUENCE_SITE_URL=https://your-site.atlassian.net
# CONFLUENCE_EMAIL=your-email@example.com
# CONFLUENCE_API_TOKEN=your-api-token

# Get a configured client
client = get_confluence_client()

# Get a page
page_id = validate_page_id("12345")
page = client.get(f"/api/v2/pages/{page_id}")
print(format_page(page))

# Create content from Markdown
content = markdown_to_adf("# Hello\n\nThis is **bold** text.")

Direct Client Usage

from confluence_as import ConfluenceClient

client = ConfluenceClient(
    base_url="https://your-site.atlassian.net",
    email="your-email@example.com",
    api_token="your-api-token",
    timeout=30,
    max_retries=3,
)

# GET request
page = client.get("/api/v2/pages/12345")

# POST request
new_page = client.post("/api/v2/pages", json_data={
    "spaceId": "123456",
    "title": "New Page",
    "body": {"representation": "atlas_doc_format", "value": "..."}
})

# Pagination
for page in client.paginate("/api/v2/pages", params={"space-id": "123456"}):
    print(page["title"])

# File upload
result = client.upload_file(
    "/api/v2/attachments",
    file_path="/path/to/file.pdf",
    additional_data={"pageId": "12345"}
)

Configuration

Environment Variables

export CONFLUENCE_SITE_URL="https://your-site.atlassian.net"
export CONFLUENCE_EMAIL="your-email@example.com"
export CONFLUENCE_API_TOKEN="your-api-token"

Error Handling

from confluence_as import (
    handle_errors,
    ConfluenceError,
    AuthenticationError,
    NotFoundError,
    ValidationError,
)

@handle_errors
def main():
    # Validation errors
    page_id = validate_page_id(user_input)  # Raises ValidationError if invalid

    # API errors are caught and formatted
    page = client.get(f"/api/v2/pages/{page_id}")

if __name__ == "__main__":
    main()  # Errors are caught, formatted, and exit with appropriate code

Validators

from confluence_as import (
    validate_page_id,      # Numeric string validation
    validate_space_key,    # Space key format
    validate_cql,          # CQL query syntax
    validate_content_type, # page, blogpost, etc.
    validate_url,          # URL format
    validate_email,        # Email format
    validate_title,        # Page title constraints
    validate_label,        # Label format
    validate_limit,        # Pagination limit
)

page_id = validate_page_id("12345")         # Returns "12345"
space_key = validate_space_key("docs")       # Returns "DOCS" (normalized)
cql = validate_cql('space = "DOCS"')         # Validates syntax

ADF Conversion

from confluence_as import (
    markdown_to_adf,
    adf_to_markdown,
    text_to_adf,
    adf_to_text,
    create_heading,
    create_paragraph,
    create_bullet_list,
)

# Convert Markdown to ADF
adf = markdown_to_adf("""
# My Document

This is a **bold** paragraph.

- Item 1
- Item 2

```python
print("Hello")

""")

Convert ADF back to Markdown

markdown = adf_to_markdown(adf)

Build ADF programmatically

from confluence_as import create_adf_doc doc = create_adf_doc([ create_heading("Title", level=1), create_paragraph(text="Hello, World!"), create_bullet_list(["Item 1", "Item 2", "Item 3"]), ])


## Caching

```python
from confluence_as import Cache, cached

# Direct cache usage
cache = Cache(default_ttl=300)  # 5 minutes
cache.set("key", {"data": "value"})
value = cache.get("key")

# Decorator usage
@cached(ttl=300)
def get_page(page_id):
    return client.get(f"/api/v2/pages/{page_id}")

# First call hits API, subsequent calls use cache
page = get_page("12345")

API Reference

Client

  • ConfluenceClient - HTTP client class
  • create_client() - Factory function
  • get_confluence_client() - Get configured client from environment variables

Config

  • ConfigManager - Configuration management class

Errors

  • ConfluenceError - Base exception
  • AuthenticationError - 401 errors
  • PermissionError - 403 errors
  • ValidationError - 400 errors
  • NotFoundError - 404 errors
  • RateLimitError - 429 errors
  • ConflictError - 409 errors
  • ServerError - 5xx errors

Formatters

  • format_page(), format_space(), format_comment()
  • format_table() - ASCII table formatting
  • format_json() - JSON formatting
  • export_csv() - CSV export
  • print_success(), print_warning(), print_info()

ADF Helper

  • markdown_to_adf(), adf_to_markdown()
  • text_to_adf(), adf_to_text()
  • create_*() - Node creation functions
  • validate_adf() - Validate ADF structure

XHTML Helper

  • xhtml_to_markdown(), markdown_to_xhtml()
  • xhtml_to_adf(), adf_to_xhtml()
  • validate_xhtml() - Validate XHTML structure

Cache

  • Cache - File-based cache class
  • cached() - Caching decorator
  • get_cache() - Get global cache instance

Contributing

Development prerequisites: pip install -e '.[dev]' includes hatchling and build; check local wheel and source builds with python -m build --no-isolation. For Base Document refreshes, install oasdiff 1.31.0: on macOS run curl --fail --location --retry 3 --output oasdiff.tar.gz https://github.com/oasdiff/oasdiff/releases/download/1.31.0/oasdiff_1.31.0_darwin_all.tar.gz; on Linux run curl --fail --location --retry 3 --output oasdiff.tar.gz https://github.com/oasdiff/oasdiff/releases/download/1.31.0/oasdiff_1.31.0_linux_amd64.tar.gz. Then run tar -xzf oasdiff.tar.gz oasdiff and chmod +x oasdiff, and place the binary on PATH (required by the refresh acceptance test), or set OASDIFF=/path/to/oasdiff for the refresh script.

  1. Fork the repository
  2. Create a feature branch
  3. Make changes with tests
  4. Submit a pull request
# Install development dependencies
pip install -e ".[dev]"

# Install pre-commit hooks (recommended)
pre-commit install

# Run tests
pytest

# Run with coverage
pytest --cov=src/confluence_as

# Code quality checks (run automatically on commit with pre-commit)
ruff check src/ tests/      # Linting
ruff format src/ tests/     # Formatting
mypy src/                   # Type checking
bandit -r src/ -q           # Security scanning

License

MIT License - see LICENSE for details.

Build

Confluence v2 (primary) and v1 (lower tier, loaded on demand) Base Documents are pinned in src/confluence_as/specs/manifest.json by source URL, declared version, SHA256 and fetch time. The JSON overlays beside them are the source of enrichment; vendored documents remain pristine. as-engine verifies the pins, strips the narrative extension, applies overlays and defect hooks, and compiles one deterministic operation index per document.

Wheel and editable builds both generate src/confluence_as/_generated/. It is ignored by git; wheels include its indexes and catalog, while source archives include the inputs and build hook. Nothing is fetched at build or runtime. Consumers use as_engine.index.load_index(path) for one document, or ProductIndexes(generated_directory) to load primary indexes eagerly and retrieve v1 on demand with .get("v1").

The runtime dependency is as-engine>=0.1.2,<0.2. Release and isolated builds use the verified as-engine==0.1.2 wheel (SHA256 ac4cb0b07effbecff812652a33aea54b036971e883d3fe349428c6d723d90d83) and hatchling==1.32.0; do not use a moving source branch as the release baseline. Install the development extra with python -m pip install --no-build-isolation -e '.[dev]', or build an sdist and wheel with python -m build --no-isolation in the prepared environment.

Refresh deliberately with python scripts/refresh_base_documents.py, with oasdiff on PATH (or OASDIFF=/path/to/oasdiff). For an offline refresh use --from-file v2=/path/to/new-v2.json; only named documents are refreshed in that mode. Each refresh appends <document>.changelog.md, then replaces the source and updates the manifest pin. Review and commit those sources and changelogs together. A missing oasdiff binary records an explicit skip in the changelog and stderr; install it and obtain the real diff before accepting a refresh. Invalid JSON, changed existing pins or a failed oasdiff command leave the sources unchanged.

Indexed API operations

Tagged API calls now require CONFLUENCE_ALLOWED_SPACES=DOCS,ENG (or settings-file confluence.allowed_spaces); absent means default deny. Body-scope calls require --space KEY, for example api call createPage --space DOCS --field 'spaceId="55"': one bounded space lookup must verify that id before the mutation. Space-filtered lists require --space-id; page-id calls resolve metadata before checking the space. Local refusals exit 4. Site-level calls such as getSpaces require explicit CONFLUENCE_ALLOW_SITE_OPERATIONS=1 (or confluence.allow_site_operations). This policy applies to tagged API calls and surviving wrapper workflows; untagged operations keep their documented existing behavior. The Jira issue creation leg in jira create-from-page uses a bounded JAS-51 createIssue operation through engine HTTPTransport; it does not establish a Jira project guard or universal Surface.call coverage. See as-engine's docs/guard.md for the exact 82-operation v2 coverage, untagged list, metadata-read exception, and request counts; the examples below require the corresponding scope flags and settings for tagged operations.

The api group calls operations from the packaged OpenAPI index through as-engine:

confluence-as api search page
confluence-as api describe createPage
confluence-as api call getPages --limit 5
confluence-as api call get-pages --space-id '[123,456]' --limit 5
confluence-as api call createPage --body @page.json
confluence-as api call createPage --body - < page.json
confluence-as api call createPage --field 'spaceId="123"' --field title=Example
confluence-as api topics

Operation IDs accept canonical camelCase or kebab-case. Spec parameters become kebab-case flags; api call OPERATION --help lists them. Booleans take explicit true/false; arrays accept repeated flags, comma-separated values or JSON arrays. --field path=value builds dotted object fields, parsing JSON values where possible; quote a numeric ID as JSON when the body schema requires a string. Invalid parameters are refused before a transport is created. --limit is the operation's spec parameter; one response page is returned.

For operations tagged for paging, --all aggregates response pages. With --all, --limit caps the total items returned and --parameter-limit sets each server page size; without it, --limit keeps its spec-defined page-size meaning. Tagged prerequisites expose their key aliases (for example --space-key DOCS) and resolve them before the call; tagged updates accept --version to override version enrichment.

Tagged page/blog body fields accept literal Markdown or UTF-8 files, for example api call updatePage --id 123 --field 'id="123"' --field title=Notes --field status=current --field body=@notes.md --confirm (on one line, with the configured space allowlist). The default write representation is storage; --representation atlas_doc_format sends stringified ADF. Existing JSON envelopes remain encoded input. Reads render tagged bodies as Markdown with lossless placeholders; --raw retains the stored body. Use the separate spec flag --body-format storage|atlas_doc_format to request read content. Copy a mention placeholder into a Markdown file and select its same representation on the next write to preserve its node. Missing files and conflicting representation options fail before lookups; --raw still converts write input. An unseeded schema-generated responder body is not a valid rich-text document; offline rich-text tests seed explicit storage/ADF responses. Surviving wrapper writes use the same tagged conversion and version pipeline.

Calls emit JSON; --format table|markdown renders the top-level result. Search defaults to a table and supports --format json; describe defaults to Markdown and supports --format json. Search excludes deprecated operations unless --include-deprecated is set; calls warn and show a replacement when enrichment provides one. Topics come from x-as-topic, with a clean empty state until tagged. Only v2 is loaded for primary discovery; a named v1 operation loads v1 on demand.

Use --validate-body to check a body before sending. Otherwise body checks run only after a 400 to enrich the error. The checker reflects the indexed schema and reports unsupported validators explicitly. Upstream createPage body alternatives overlap: its body oneOf may reject a valid service payload until a correcting overlay lands. Ordinary calls do not run that optional body check.

HTTP uses the existing configuration chain (CONFLUENCE_SITE_URL, CONFLUENCE_EMAIL, CONFLUENCE_API_TOKEN), timeouts, TLS settings and retry settings. The engine supplies pooled HTTP with 429/5xx backoff and Retry-After support. Confluence's existing domain error mapper is reused. JSON bodies are supported; non-JSON media types are explicitly refused pending JAS-61. Legacy single-operation commands now return migration hints; workflow survivors remain available.

Offline responder mode uses the same call path without reading credentials:

CONFLUENCE_AS_TRANSPORT=responder confluence-as api call getPages --limit 5
CONFLUENCE_AS_TRANSPORT=responder confluence-as api --respond-with 400 call getPages

The responder returns indexed examples or bounded schema-generated bodies, never a live request. It is stateless and returns null where no 200 schema/example survives in the index. --respond-with is a test hook accepted only in responder mode.

Errors are JSON on stderr: {status, messages, operation, note}. Exit codes are usage/400 2, auth 3, permission/scope 4, not found 5, server/exhausted rate limit 6, other failure (including 409) 1, success 0. No Confluence project guard is introduced. Live-site acceptance is held separately from offline responder tests pending the Confluence sandbox ruling.

Progressive help

Run confluence-as or confluence-as help for the surface map, help api for discovery commands, and help adf or help paging for tagged gotchas. api describe deletePage shows parameters, body and risk; --full includes the complete description, and --examples shows enrichment invocations and bodies. Wrapper <group> <verb> --help uses the same renderer, with --full and --examples. Add --format json to help/describe for the same content in a structured document. Large topic/group lists show --offset N to continue. help search loads the v1 CQL topic on demand; help risk --tier v1 explicitly loads lower-tier help. api describe resolves lower-tier operation IDs on demand.

Operations tagged destructive or irreversible preview by default: for example, api call deletePage --id 123 prints JSON and sends no request. Add --confirm to send through the normal guard and transform pipeline. Previews validate local inputs but do not perform prerequisite or version lookups; unresolved requirements are identified in the output. Direct Python Surface calls retain their existing behavior. Discovery needs no credentials. API responder/cassette modes are distinct from the stateful simulation mode. CONFLUENCE_MOCK_MODE applies to Python integrations that still use the legacy client. The Confluence calls in jira create-from-page use indexed operations and the selected engine transport; indexed API calls support the responder, cassette, simulation and HTTP transport modes.

Help snapshots live in tests/golden/help/. Regenerate with UPDATE_HELP_GOLDEN=1 .venv/bin/python -m pytest -q -p no:cacheprovider tests/test_help.py and review the diff. Tests enforce ceil(characters/4) Markdown caps of 400/800/1200/600 tokens for Levels 0/1/2/3 and 800 for the topic list; --full is intentionally uncapped. JSON serialization metadata is not counted again. Regeneration never disables cap checks.

Wrapper workflows and migration

The reviewed 108-verb inventory retains 38 workflows, replaces 70 verbs with index-backed migration hints, and has no deferred implementations. See the full decision table for every old command, reason, replacement invocation and disposition. For example, executing page create --space DOCS --title T now emits JSON on stderr, exits 2, and points to api call createPage; page get --help still succeeds. help migration discovers the rename topic.

Survivors cover bulk selection/checkpoint/resume, hierarchy traversal/copy, CSV/history/cache/report transforms, permission decisions and Jira macro workflows. Confluence requests from surviving workflows use Surface and the same scope/tag pipeline as api, except for Jira issue creation in jira create-from-page. That bounded JAS-51 Jira leg uses engine HTTPTransport without a Jira project guard; see its documented seam above. Bulk dry-run resolves targets using reads, prints intended operations, and sends no writes. Both bulk label add --labels reviewed --cql 'space=DOCS' --dry-run and bulk label --add reviewed --cql 'space=DOCS' --dry-run are supported.

For offline stateful workflows set CONFLUENCE_AS_TRANSPORT=simulation and an explicit space allowlist (plus site-operation permission where required). The simulation starts a fresh store per process and never reads credentials or sends HTTP; tests may inject a shared store for sequential runs. The schema responder remains stateless. See the engine's docs/simulation.md for supported operations, CQL and explicit failures. attachment download ATTACHMENT_ID resolves safe metadata through the Surface and writes the binary response; use --all with a page ID to download every attachment into --output-dir (also accepted as --output or -o). jira create-from-page creates the Jira issue with the JAS-51 hand-built createIssue operation through engine HTTPTransport, with the existing 30-second timeout, zero retries, flags and output, and no jira-as dependency. Its Confluence page reads and marker update use indexed engine operations and the Confluence scope guard. The Jira request is a bounded transport exception: it has no Jira project guard and is not universal Surface.call coverage. A cross-product operation is not transactional; if Jira creation succeeds and the Confluence update fails, inspect the reported issue before retrying.

1.x support

The 1.x branch receives security and critical fixes only until one quarter after the 2.0.0 release date, 2026-10-06; support ends on 2027-01-06.

Metadata

Release files for confluence-as 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for confluence-as 2.0.0
File Size Uploaded Provenance Actions
confluence_as-2.0.0.tar.gz 242.2 kB With provenance Details for confluence_as-2.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for confluence-as 2.0.0
File Interpreter ABI Platform Actions
confluence_as-2.0.0-py3-none-any.whl Python 3 none any Details for confluence_as-2.0.0-py3-none-any.whl

Total release size: 595.3 kB

Release files / confluence_as-2.0.0.tar.gz

Download URL confluence_as-2.0.0.tar.gz
Size 242.2 kB
Tags Source
SHA-256 checksum
How to use checksums
85785e4fa1e23eee752ca5ec3c2144a13e41fc69e7dcd69548fb21ae14e82df1
BLAKE2b-256 checksum
How to use checksums
56f6b3b5b63707966dfeffeb21077fab24ae4ce5d1f1d9cb232d23a14492ed86
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.

Transparency log

Release files / confluence_as-2.0.0-py3-none-any.whl

Download URL confluence_as-2.0.0-py3-none-any.whl
Size 353.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
07c22a4f99dd4187f477e1fdd897991ebe41544610bd7f46ec6163fc46b9bd9d
BLAKE2b-256 checksum
How to use checksums
1f6b16044a15bec8c73ff3eb859689a9d7f5f2e6a9df787a5469336c8419b271
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.

Transparency log

Security for confluence-as 2.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for confluence_as-2.0.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page