Skip to main content
The Mesa Python REST SDK (mesa-rest) provides a typed Python client for interacting with the Depot API. It follows the same OpenAPI contract as the TypeScript SDK, with resource-based accessors, pagination support, retries, and structured errors.

Installation

pip install mesa-rest

Quick Start

import os
from mesa_rest import Mesa

client = Mesa(
    api_key=os.environ["MESA_API_KEY"],
)

# Create a repository
repo = client.repos.create(
    org="my-org",
    body={"name": "my-repo"},
)

# List repositories
repos_page = client.repos.list(org="my-org")
print(repos_page)

Configuration

You can configure authentication, server URL, timeout, and retries when constructing the client.
from mesa_rest import Mesa

client = Mesa(
    # Required: API key for authentication
    api_key="your-api-key",

    # Optional: Override base URL
    server_url="https://api.mesa.dev/v1",

    # Optional: Global timeout in milliseconds
    timeout_ms=30_000,

    # Optional: Retry configuration
    retry_config={
        "strategy": "backoff",
        "backoff": {
            "initial_interval": 500,
            "max_interval": 60_000,
            "exponent": 1.5,
            "max_elapsed_time": 300_000,
        },
    },
)

Environment Variables

The SDK can read credentials from environment variables when not explicitly configured:
  • MESA_API_KEY - your API key

Resources

The SDK organizes methods into resource namespaces that mirror the API structure.

Repositories

# Create a repository
repo = client.repos.create(
    org="my-org",
    body={
        "name": "my-repo",
        "default_bookmark": "main",  # optional
    },
)

# List repositories (paginated)
repos_page = client.repos.list(
    org="my-org",
    limit=50,
    cursor="...",  # optional
)

# Get a specific repository
repo = client.repos.get(
    org="my-org",
    repo="my-repo",
)

# Update a repository
updated = client.repos.update(
    org="my-org",
    repo="my-repo",
    body={"name": "new-name"},
)

# Delete a repository
client.repos.delete(
    org="my-org",
    repo="my-repo",
)

Bookmarks

# List bookmarks
bookmarks_page = client.bookmarks.list(
    org="my-org",
    repo="my-repo",
)

# Create a bookmark
bookmark = client.bookmarks.create(
    org="my-org",
    repo="my-repo",
    body={
        "name": "feature-bookmark",
        "change_id": "xyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxy",
    },
)

# Delete a bookmark
client.bookmarks.delete(
    org="my-org",
    repo="my-repo",
    bookmark="feature-bookmark",
)

Changes

# List changes
changes_page = client.changes.list(
    org="my-org",
    repo="my-repo",
    bookmark="main",  # optional
)

# Get a change
change = client.changes.get(
    org="my-org",
    repo="my-repo",
    change_id="xyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxy",
)

# Create a change
new_change = client.changes.create(
    org="my-org",
    repo="my-repo",
    body={
        "base_change_id": "xyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxy",
        "message": "Add README",
        "author": {
            "name": "Bot",
            "email": "bot@example.com",
        },
        "files": [
            {
                "path": "README.md",
                "action": "upsert",
                "content": "IyBIZWxsbyBmcm9tIE1lc2EK",
                "encoding": "base64",
            }
        ],
    },
)

Content

# Get file content
content = client.content.get(
    org="my-org",
    repo="my-repo",
    path="src/index.py",
    ref="main",  # optional
)

# Handle file vs directory responses
if getattr(content, "type", None) == "file":
    print(content.content)
else:
    print(content.entries)

Diffs

diff = client.diffs.get(
    org="my-org",
    repo="my-repo",
    base_change_id="xyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxy",
    head_change_id="wvutsrqponmlkzyxwvutsrqponmlkzyx",
)

for file in diff.files:
    print(f"{file.change_type}: {file.path}")

Merge

# Merge a branch into another (fast-forward when possible)
result = client.merge.perform(
    org="my-org",
    repo="my-repo",
    base="main",
    body={
        "head": "feature-branch",
        "author": {
            "name": "Bot",
            "email": "bot@example.com",
        },
    },
)

print(result.merge_type)  # "fast_forward" or "merge_commit"
print(result.sha)          # Resulting commit SHA
# Merge with a custom commit message
result = client.merge.perform(
    org="my-org",
    repo="my-repo",
    base="main",
    body={
        "head": "feature-branch",
        "message": "Ship feature X",
        "author": {
            "name": "Bot",
            "email": "bot@example.com",
        },
    },
)
# Merge and delete the head branch afterward
result = client.merge.perform(
    org="my-org",
    repo="my-repo",
    base="main",
    body={
        "head": "feature-branch",
        "author": {
            "name": "Bot",
            "email": "bot@example.com",
        },
        "delete_branch": True,
    },
)

print(result.branch_deleted)  # True
The merge endpoint returns 409 MERGE_CONFLICT when the branches have conflicting changes that cannot be auto-resolved. Catch this error to handle conflicts in your workflow.

Admin (API Keys)

# Create API key
new_key = client.admin.create_api_key(
    org="my-org",
    body={
        "name": "CI Key",
        "scopes": ["write"],
    },
)
print(new_key.key)

# List API keys
keys = client.admin.list_api_keys(org="my-org")

# Revoke API key
client.admin.revoke_api_key(
    org="my-org",
    id="key_123",
)

Pagination

List endpoints support cursor-based pagination.
cursor = None

while True:
    page = client.repos.list(
        org="my-org",
        cursor=cursor,
        limit=100,
    )

    for repo in page.result.repos:
        print(repo.name)

    cursor = page.result.next_cursor
    if not cursor:
        break

Error Handling

The SDK raises exceptions for request and response failures. Catch and branch on error type or status as needed by your application.
try:
    client.repos.get(org="my-org", repo="missing")
except Exception as exc:
    # Handle SDK-specific errors in your application layer.
    # Common categories: auth errors, validation errors, not found,
    # conflict, and transport failures.
    print(f"Request failed: {exc}")
    raise

Error Shape

API errors follow a consistent shape:
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Repository not found",
    "details": {}
  }
}

Per-Request Options

You can override client defaults for a single request (for example timeout, retries, or headers).
repo = client.repos.get(
    org="my-org",
    repo="my-repo",
    request_options={
        "timeout_ms": 60_000,
        "headers": {
            "X-Custom-Header": "value",
        },
    },
)

Complete Example

import os
from mesa_rest import Mesa


def main() -> None:
    client = Mesa(api_key=os.environ["MESA_API_KEY"])

    # Create repository
    repo = client.repos.create(
        org="my-org",
        body={"name": "example-repo"},
    )

    # Initial change on main
    main_change = client.changes.create(
        org="my-org",
        repo="example-repo",
        body={
            "base_change_id": repo.head_change_id,
            "message": "Initial change",
            "author": {
                "name": "Bot",
                "email": "bot@example.com",
            },
            "files": [
                {
                    "path": "README.md",
                    "action": "upsert",
                    "content": "IyBFeGFtcGxlIFJlcG9zaXRvcnkK",
                    "encoding": "base64",
                }
            ],
        },
    )

    # Create feature bookmark from the unpublished main change head
    client.bookmarks.create(
        org="my-org",
        repo="example-repo",
        body={
            "name": "feature",
            "change_id": main_change.id,
        },
    )

    # Create a feature change
    client.changes.create(
        org="my-org",
        repo="example-repo",
        body={
            "base_change_id": main_change.id,
            "message": "Add feature",
            "author": {
                "name": "Bot",
                "email": "bot@example.com",
            },
            "files": [
                {
                    "path": "feature.py",
                    "action": "upsert",
                    "content": "RkVBVFVSRV9FTkFCTEVEID0gVHJ1ZQo=",
                    "encoding": "base64",
                }
            ],
        },
    )

    # Diff feature against main
    diff = client.diffs.get(
        org="my-org",
        repo="example-repo",
        base="main",
        head="feature",
    )

    print(f"Changes: {len(diff.files)} files")

    # Merge feature branch into main and clean up
    merge = client.merge.perform(
        org="my-org",
        repo="example-repo",
        base="main",
        body={
            "head": "feature",
            "author": {
                "name": "Bot",
                "email": "bot@example.com",
            },
            "delete_branch": True,
        },
    )

    print(f"Merged via {merge.merge_type}: {merge.sha}")


if __name__ == "__main__":
    main()
For endpoint behavior and payload details shared across all SDKs, see the HTTP API Overview.