Skip to main content

Python client library and CLI for the Adapts API โ€” chat with your codebase wikis, generate docs, and more.

Project description

AdaptsAPI Client

A Python client library and CLI for the Adapts API: chat with your codebase wikis, generate wiki docs, and generate release notes.

PyPI version Python 3.12+ License: Proprietary Test Coverage

Features

  • ๐Ÿš€ AdaptsClient โ€” high-level client with chat(), list_chat_enabled_wikis(), list_all_wikis(), generate_docs()
  • ๐Ÿ’ฌ Chat with wikis โ€” async start, poll for status, multi-wiki, same-thread follow-ups
  • ๐Ÿ“„ Generate docs & release notes โ€” submit repos for wiki generation
  • ๐Ÿ”‘ Token loading via ADAPTS_API_TOKEN, ADAPTS_API_KEY, config.json, or interactive prompt
  • ๐Ÿ“ฆ CLI: adaptsapi (--chat | --chat-status | --chat-enabled-wikis | --list-wikis | --generatedoc | --releasenotes) โ€ฆ
  • โœ… Validation: Pydantic models for chat and generate-docs payloads
  • ๐Ÿงช Test suite with high coverage; integration demos under tests/integ/

Installation

From PyPI

pip install adaptsapi

From Source

git clone https://github.com/AdaptsAI/adapts_api_client.git
cd adapts_api_client
pip install -e .

Quick Start

1. Set up your API token

load_token() resolves credentials in this order:

  1. ADAPTS_API_TOKEN or ADAPTS_API_KEY (recommended for CI/CD)
  2. token in ./config.json
  3. Interactive prompt (CLI saves token to config.json)
export ADAPTS_API_TOKEN="your-api-token-here"
# or: export ADAPTS_API_KEY="your-api-token-here"

Optional config.json (same directory you run the CLI from) for base URL and per-operation paths:

{
  "token": "your-api-token-here",
  "base_url": "https://prod.api.adapts.ai",
  "generate_wiki_docs_metadata": {
    "path": "/generate_wiki_docs"
  },
  "generate_release_notes_metadata": {
    "path": "/generate_release_notes"
  }
}

You can set a full URL per operation with "url" instead of "path". Legacy endpoint (full generate-docs URL, or host-only base) is still supportedโ€”see Configuration.

First-time CLI without env or token in config:

adaptsapi --generatedoc --data '{"test": "payload"}'
# Prompts for token and merges into config.json

2. Using the Python client

from adaptsapi import AdaptsClient

client = AdaptsClient()  # uses ADAPTS_API_KEY env var

# List wikis available for chat
wikis = client.list_chat_enabled_wikis()

# Chat (blocks until response is ready)
response = client.chat("What does this repo do?", wiki_name="my-repo_main")
print(response["response"])

# Chat across multiple wikis
response = client.chat("Compare these repos.", wiki_names=["wiki_a", "wiki_b"])

# Async chat (returns immediately, poll manually)
started = client.chat_async("Explain the auth flow.", wiki_name="my-repo_main")
status = client.poll_chat_status(started["thread_id"], started["created_on"])

# List all wiki generation requests
all_wikis = client.list_all_wikis()
# Generate wiki documentation
client.generate_docs(
    email="you@example.com",
    user_name="your_username",
    repo_name="my-repo",
    repo_url="https://github.com/user/my-repo",
    branch="main",
)

3. Using the CLI

Omit --endpoint when config.json supplies defaults (see above).

Generate docs (inline JSON):

adaptsapi --generatedoc \
  --endpoint "https://prod.api.adapts.ai/generate_wiki_docs" \
  --data '{"email_address": "user@example.com", "user_name": "john_doe", "repo_object": {...}}'

Generate docs (payload file):

adaptsapi --generatedoc \
  --endpoint "https://prod.api.adapts.ai/generate_wiki_docs" \
  --payload-file payload.json

Chat:

adaptsapi --chat --data '{"user_prompt":"What does this do?","wiki_name":"my-repo_main"}'
adaptsapi --chat-status --data '{"thread_id":"...","created_on":"..."}'
adaptsapi --chat-enabled-wikis
adaptsapi --list-wikis

Release notes (same payload as wiki docs; uses config default or --endpoint):

adaptsapi --releasenotes --payload-file payload.json

Testing

The suite includes unit tests, CLI/config tests, and optional integration demos.

Running tests

# Full check (pytest + lint + mypy) โ€” same as `make test`
python run_tests.py --type all

# Makefile (uses `.venv/bin/python` when present)
make test

Pytest only:

python -m pytest
python -m pytest -v
python -m pytest tests/test_generate_docs.py
python -m pytest --cov=src/adaptsapi --cov-report=html

Test runner:

python run_tests.py --type unit
python run_tests.py --type integration   # needs ADAPTS_API_TOKEN / ADAPTS_API_KEY for live calls
python run_tests.py --type coverage
python run_tests.py --type integration --api-key "your-api-key"

Categories

  • Unit: mocked HTTP and fast paths
  • Integration / API: real calls when marked @pytest.mark.integration or @pytest.mark.api and credentials are set
  • tests/integ/: runnable demos (test_chat_demo.py, test_get_list_of_all_wikis_demo.py, test_generate_docs_demo.py, test_workflow.py)

Test Coverage

The test suite covers:

  • โœ… Payload Validation: All validation logic and error cases
  • โœ… Metadata Population: Automatic metadata generation
  • โœ… API Calls: HTTP request handling and error management
  • โœ… CLI Functionality: Command-line argument parsing and file handling
  • โœ… Configuration: Token loading and config file management

Integration demos

Runnable modules under tests/integ/ (pytest or python tests/integ/...):

  • test_chat_demo.py โ€” chat validation, mocked POST, optional live call
  • test_get_list_of_all_wikis_demo.py โ€” list wikis, mocked POST, optional live call
  • test_generate_docs_demo.py โ€” validation, metadata, optional live generate-docs call
  • test_workflow.py โ€” end-to-end style walkthrough

Set ADAPTS_API_TOKEN or ADAPTS_API_KEY for live requests.

Usage

Command Line Options

adaptsapi (--chat | --chat-status | --chat-enabled-wikis | --list-wikis | --generatedoc | --releasenotes) [OPTIONS]
Option Description Required
--chat POST a chat request (user_prompt, wiki_name/wiki_names) One mode required
--chat-status Poll chat status (thread_id, created_on) One mode required
--chat-enabled-wikis List wikis available for chat (no payload) One mode required
--list-wikis List all wiki generation requests (no payload) One mode required
--generatedoc POST a generate-docs / wiki payload One mode required
--releasenotes POST a release-notes payload One mode required
--endpoint URL Full API URL No (defaults from config.json)
--data JSON Inline JSON payload string Yes (or --payload-file; not needed for --chat-enabled-wikis / --list-wikis)
--payload-file FILE Path to JSON payload file Yes (or --data)
--timeout SECONDS Request timeout in seconds (default: 30) No

Payload Structure

For documentation generation, your payload should follow this structure:

{
  "email_address": "user@example.com",
  "user_name": "github_username",
  "repo_object": {
    "repository_name": "my-repo",
    "source": "github",
    "repository_url": "https://github.com/user/my-repo",
    "branch": "main",
    "size": "12345",
    "language": "python",
    "is_private": false,
    "git_provider_type": "github",
    "refresh_token": "github_token_here"
  }
}

Required Fields

  • email_address: Valid email address
  • user_name: Username string
  • repo_object.repository_name: Repository name
  • repo_object.repository_url: Full repository URL
  • repo_object.branch: Branch name
  • repo_object.size: Repository size as string
  • repo_object.language: Primary programming language
  • repo_object.source: Source platform (e.g., "github")

Optional Fields

  • repo_object.is_private: Boolean indicating if repo is private
  • repo_object.git_provider_type: Git provider type
  • repo_object.installation_id: Installation ID (for GitHub Apps)
  • repo_object.refresh_token: Refresh token for authentication
  • repo_object.commit_hash: Specific commit hash
  • repo_object.commit_message: Commit message
  • repo_object.commit_author: Commit author
  • repo_object.directory_name: Specific directory to process
  • repo_object.previous_commit_hash: Previous commit hash (common for release notes)
  • repo_object.pr_number, pr_title, pr_body, pr_url: Pull request context (strings)

GitHub Actions Integration

This package is designed to work seamlessly with GitHub Actions for automated documentation generation. Here's an example workflow:

name: Generate Wiki Docs

on:
  pull_request:
    branches: [ main ]
    types: [ closed ]

jobs:
  call-adapts-api:
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          
      - name: Install adaptsapi
        run: pip install adaptsapi
        
      - name: Generate documentation
        env:
          ADAPTS_API_TOKEN: ${{ secrets.ADAPTS_API_TOKEN }}
        run: |
          python -c "
          import os
          from adaptsapi.generate_docs import post
          
          payload = {
              'email_address': '${{ github.actor }}@users.noreply.github.com',
              'user_name': '${{ github.actor }}',
              'repo_object': {
                  'repository_name': '${{ github.event.repository.name }}',
                  'source': 'github',
                  'repository_url': '${{ github.event.repository.html_url }}',
                  'branch': 'main',
                  'size': '0',
                  'language': 'python',
                  'is_private': False,
                  'git_provider_type': 'github',
                  'refresh_token': '${{ secrets.GITHUB_TOKEN }}'
              }
          }
          
          resp = post(
              'https://prod.api.adapts.ai/generate_wiki_docs',
              os.environ['ADAPTS_API_TOKEN'],
              payload
          )
          resp.raise_for_status()
          print('Documentation generated successfully')
          "

Setting up GitHub Secrets

  1. Go to your repository on GitHub
  2. Click Settings โ†’ Secrets and variables โ†’ Actions
  3. Click New repository secret
  4. Add ADAPTS_API_TOKEN (recommended) or ADAPTS_API_KEY with your API token value

If the repository already stores the token under ADAPTS_API_KEY, map it in the workflow: ADAPTS_API_TOKEN: ${{ secrets.ADAPTS_API_KEY }}.

Configuration

config.json

Placed in the current working directory. Common keys:

Key Purpose
token API key (if not using env)
base_url / api_base_url API host; paths below are appended
generate_wiki_docs_metadata { "url": "..." } or { "path": "/generate_wiki_docs" }
generate_release_notes_metadata { "url": "..." } or { "path": "/generate_release_notes" }
endpoint Legacy full generate-docs URL, or host-only base

Override the host with ADAPTS_API_BASE_URL.

Example (paths + base):

{
  "token": "your-api-token-here",
  "base_url": "https://prod.api.adapts.ai",
  "generate_wiki_docs_metadata": { "path": "/generate_wiki_docs" }
}

Example (legacy single endpoint):

{
  "token": "your-api-token-here",
  "endpoint": "https://prod.api.adapts.ai/generate_wiki_docs"
}

Environment variables

Variable Purpose
ADAPTS_API_TOKEN / ADAPTS_API_KEY API key (load_token() checks both)
ADAPTS_API_BASE_URL Base URL when not set in config.json

Error Handling

The library provides comprehensive error handling:

  • PayloadValidationError: Raised when payload validation fails
  • ConfigError: Raised when no token can be found or loaded
  • requests.RequestException: Raised on network failures
  • JSONDecodeError: Raised for invalid JSON in config files

Common Error Scenarios

  • Missing token: CLI prompts for interactive token input
  • Invalid JSON: Shows JSON parsing errors
  • API errors: Displays HTTP status codes and error messages
  • Payload validation: Shows specific validation failures with field names

Development

Prerequisites

  • Python 3.12+
  • pip

Setup Development Environment

git clone https://github.com/AdaptsAI/adapts_api_client.git
cd adapts_api_client
python -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
pip install -r requirements-dev.txt
pip install -e .

Development Dependencies

Install development dependencies for testing and code quality:

pip install -r requirements-dev.txt

This includes:

  • pytest - Testing framework
  • pytest-cov - Coverage reporting
  • pytest-mock - Mocking utilities
  • flake8 - Code linting
  • mypy - Type checking
  • black - Code formatting
  • isort - Import sorting

Running tests

python -m pytest
python -m pytest --cov=src/adaptsapi --cov-report=html
python -m pytest -m "not integration"
python -m pytest -m "integration"
python run_tests.py --type all
make test          # same as run_tests.py --type all
make build         # test then python -m build

Code Quality

# Lint code
flake8 src/adaptsapi tests/

# Type checking
mypy src/adaptsapi/

# Format code
black src/adaptsapi tests/
isort src/adaptsapi tests/

Publishing to PyPI

Prerequisites

Before publishing to PyPI, ensure you have:

  1. PyPI Account: Create an account at pypi.org
  2. TestPyPI Account: Create an account at test.pypi.org
  3. API Tokens: Generate API tokens for both PyPI and TestPyPI
  4. Build Tools: Install required build tools
pip install build twine

Build Configuration

The project uses pyproject.toml for build configuration. Key settings:

[project]
name = "adaptsapi"
version = "0.2.0"
description = "Python client library and CLI for the Adapts API"
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.12"
authors = [
  { name = "VerifyAI Inc.", email = "dev@adapts.ai" }
]
dependencies = [
  "requests",
  "pydantic[email]>=2.4",
]

Publishing Steps

1. Prepare for Release

# Clean previous builds
rm -rf build/ dist/ *.egg-info src/*.egg-info

# Update version in pyproject.toml and setup.py
# Edit version numbers in both files

# Run all tests to ensure everything works
python run_tests.py --type all

# Check code quality
flake8 src/adaptsapi tests/
mypy src/adaptsapi/

2. Build Distribution Packages

# Build source distribution and wheel
python -m build

# Verify the built packages
ls -la dist/
# Should show: adaptsapi-0.2.0.tar.gz and adaptsapi-0.2.0-py3-none-any.whl

3. Test on TestPyPI (Recommended)

# Upload to TestPyPI first
python -m twine upload --repository testpypi dist/*

# Test installation from TestPyPI
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ adaptsapi

# Test the installed package
python -c "from adaptsapi.generate_docs import post; print('โœ… Package works!')"

4. Publish to PyPI

# Upload to PyPI
python -m twine upload dist/*

# Verify installation from PyPI
pip install adaptsapi

# Test the installed package
python -c "from adaptsapi.generate_docs import post; print('โœ… Package published successfully!')"

Automated Publishing with GitHub Actions

Create .github/workflows/publish.yml:

name: Publish to PyPI

on:
  release:
    types: [published]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          
      - name: Install dependencies
        run: |
          pip install build twine
          pip install -r requirements-dev.txt
          
      - name: Run tests
        run: |
          python run_tests.py --type unit
          
      - name: Build package
        run: python -m build
        
      - name: Publish to TestPyPI
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
        run: |
          python -m twine upload --repository testpypi dist/*
          
      - name: Publish to PyPI
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
        run: |
          python -m twine upload dist/*

Environment Variables for CI/CD

Set these secrets in your GitHub repository:

  • PYPI_API_TOKEN: Your PyPI API token
  • TEST_PYPI_API_TOKEN: Your TestPyPI API token

Version Management

Semantic Versioning

Follow semantic versioning:

  • MAJOR.MINOR.PATCH (e.g., 0.2.0)
  • MAJOR: Breaking changes
  • MINOR: New features, backward compatible
  • PATCH: Bug fixes, backward compatible

Update Version Numbers

Update version in these files:

  1. pyproject.toml:

    [project]
    version = "0.2.1"  # Update this
    
  2. setup.py:

    setup(
        name="adaptsapi",
        version="0.2.1",  # Update this
        # ...
    )
    

Pre-release Checklist

Before publishing, ensure:

  • All tests pass: python run_tests.py --type all
  • Code is linted: flake8 src/adaptsapi tests/
  • Type checking passes: mypy src/adaptsapi/
  • Documentation is updated
  • Version numbers are updated in pyproject.toml, setup.py, setup.cfg, and src/adaptsapi/__init__.py
  • CHANGELOG is updated
  • Package builds successfully: python -m build
  • Package installs correctly: pip install dist/*.whl

Troubleshooting

Common Issues

  1. "File already exists" error:

    # Clean previous builds
    rm -rf build/ dist/ *.egg-info src/*.egg-info
    python -m build
    
  2. Authentication errors:

    # Check your API token
    python -m twine check dist/*
    
  3. Package not found after upload:

  4. Import errors after installation:

    # Verify package structure
    pip show adaptsapi
    python -c "import adaptsapi; print(adaptsapi.__file__)"
    

Rollback Strategy

If you need to rollback a release:

  1. Delete the release (if within 24 hours):

    # Use PyPI web interface to delete the release
    # Go to: https://pypi.org/manage/project/adaptsapi/releases/
    
  2. Yank the release (recommended):

    python -m twine delete --username __token__ --password $PYPI_API_TOKEN adaptsapi 0.2.0
    
  3. Publish a new patch version with fixes

Security Best Practices

  1. Use API tokens instead of username/password
  2. Store tokens securely in environment variables or secrets
  3. Use TestPyPI for testing before production
  4. Verify package contents before uploading
  5. Keep dependencies updated and secure

Package Verification

After publishing, verify the package:

pip install adaptsapi

python -c "
from adaptsapi import AdaptsClient, __version__
from adaptsapi.chat import post_chat
from adaptsapi.generate_docs import post
print(f'adaptsapi {__version__} โ€” all imports OK')
"

adaptsapi --help

API reference

adaptsapi.generate_docs

  • post(endpoint, token, payload, timeout=30) โ€” Wiki docs only (action: code_to_wiki); validate, populate metadata, POST with x-api-key.
  • default_generate_docs_url() โ€” Generate-docs URL from config (load_default_endpoint()), or None.

Private helpers _validate_payload and _populate_metadata are shared with release notes (schema + metadata).

adaptsapi.release_notes

  • post(endpoint, token, payload, timeout=30) โ€” Release notes (action: generate_release_notes); same transport as generate-docs.
  • default_release_notes_url() โ€” Release-notes URL from config (load_release_notes_endpoint()), or None.

adaptsapi.chat

  • post_chat(endpoint, token, payload) โ€” Start a chat (returns thread_id + created_on).
  • post_chat_status(endpoint, token, payload) โ€” Poll for chat response.
  • post_chat_enabled_wikis(endpoint, token) โ€” List wikis available for chat.

adaptsapi.get_list_of_all_wikis

  • post(endpoint, token) โ€” List all wiki generation requests.

adaptsapi.config

  • load_token() โ€” ADAPTS_API_TOKEN / ADAPTS_API_KEY, then config.json token, then prompt.
  • load_api_base_url() โ€” ADAPTS_API_BASE_URL, then config, then default prod base.
  • load_chat_url(), load_chat_status_url(), load_chat_enabled_wikis_url(), load_get_list_of_all_wikis_url() โ€” Endpoint URLs from config or defaults.

CLI

  • adaptsapi.cli.main() โ€” Entry point; requires one of --chat, --chat-status, --chat-enabled-wikis, --list-wikis, --generatedoc, or --releasenotes.

Project structure

adapts_api_client/
โ”œโ”€โ”€ src/adaptsapi/
โ”‚   โ”œโ”€โ”€ __init__.py          # AdaptsClient, AdaptsAPIError, __version__
โ”‚   โ”œโ”€โ”€ client.py            # High-level AdaptsClient
โ”‚   โ”œโ”€โ”€ chat.py              # Chat API (post_chat, post_chat_status, post_chat_enabled_wikis)
โ”‚   โ”œโ”€โ”€ get_list_of_all_wikis.py
โ”‚   โ”œโ”€โ”€ generate_docs.py     # Wiki-docs client
โ”‚   โ”œโ”€โ”€ release_notes.py     # Release-notes client
โ”‚   โ”œโ”€โ”€ cli.py
โ”‚   โ”œโ”€โ”€ config.py
โ”‚   โ””โ”€โ”€ models/              # Pydantic models (ChatRequest, ChatStatusRequest, โ€ฆ)
โ”œโ”€โ”€ tests/
โ”‚   โ”œโ”€โ”€ conftest.py
โ”‚   โ”œโ”€โ”€ integ/               # Demos: chat, list_wikis, generate_docs, workflow
โ”‚   โ”œโ”€โ”€ test_chat.py
โ”‚   โ”œโ”€โ”€ test_client.py
โ”‚   โ”œโ”€โ”€ test_get_list_of_all_wikis.py
โ”‚   โ”œโ”€โ”€ test_generate_docs.py
โ”‚   โ”œโ”€โ”€ test_release_notes.py
โ”‚   โ”œโ”€โ”€ test_cli.py
โ”‚   โ””โ”€โ”€ test_config.py
โ”œโ”€โ”€ Makefile
โ”œโ”€โ”€ run_tests.py
โ”œโ”€โ”€ requirements-dev.txt
โ”œโ”€โ”€ pytest.ini
โ””โ”€โ”€ TESTING.md

License

This software is licensed under the Adapts API Use-Only License v1.0. See LICENSE for details.

Key restrictions:

  • โœ… Use the software as-is
  • โŒ No modifications allowed
  • โŒ No redistribution allowed
  • โŒ Commercial use restrictions apply

Support

Changelog

v0.2.0 (latest)

  • AdaptsClient โ€” high-level client (chat, chat_async, poll_chat_status, list_chat_enabled_wikis, list_all_wikis, generate_docs)
  • Chat API: post_chat, post_chat_status, post_chat_enabled_wikis in adaptsapi.chat
  • get_list_of_all_wikis module
  • CLI: --chat, --chat-status, --chat-enabled-wikis, --list-wikis
  • Pydantic models: ChatRequest, ChatStatusRequest

v0.1.9

  • Prompt API client, Pydantic models, config.json metadata blocks
  • Release notes module, CLI --releasenotes
  • load_token() accepts ADAPTS_API_TOKEN or ADAPTS_API_KEY

v0.1.4 and earlier

  • Generate-docs client, CLI, tests, and legacy endpoint-only configuration

ยฉ 2025 AdaptsAI All rights reserved.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

adaptsapi-0.2.0.tar.gz (40.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

adaptsapi-0.2.0-py3-none-any.whl (27.2 kB view details)

Uploaded Python 3

File details

Details for the file adaptsapi-0.2.0.tar.gz.

File metadata

  • Download URL: adaptsapi-0.2.0.tar.gz
  • Upload date:
  • Size: 40.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for adaptsapi-0.2.0.tar.gz
Algorithm Hash digest
SHA256 43504e8e4a8a5a8cbb41f580bcd01ab1237b6242e62f2c2349971fc7089889ad
MD5 f8bbc7090cba2877f529f1f13be63352
BLAKE2b-256 c6467166581bf56c020ff7e54dea64d41271793127b8000f47106cf14eeba740

See more details on using hashes here.

File details

Details for the file adaptsapi-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: adaptsapi-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 27.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for adaptsapi-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c94713f3ed89326802ba8b6ca6669ec3e0799b2ea5573c959e28eed225490e50
MD5 06744caa9cb9ca5af7609cee578593e9
BLAKE2b-256 58dccadb8f651e52e8457fdf787d7fba1c0405900fa33c6d99e0ed98dbd614d2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page