Skip to main content

Unofficial, community-maintained typed Python client for the MURAL Public API (OpenAPI 3.1 spec-accurate)

Project description

unofficial-mural-python-client

Unofficial, community-maintained project. Not affiliated with, endorsed by, or supported by MURAL. For official resources, see developers.mural.co.

A typed Python client for the MURAL Public API, covering all 76 endpoints of the OpenAPI 3.1 spec — murals, widgets (all 10 types), rooms, workspaces, templates, search, and user identity.

  • Spec-accurate: every request/response model is a pydantic v2 model hand-derived from the spec — camelCase wire format, snake_case Python attributes, enums as Literals.
  • Client-side validation: request bodies are validated before the HTTP call, so malformed input fails fast instead of via a 400.
  • OAuth2 built in: authorization-code flow, automatic token refresh on 401, and an interactive localhost helper for getting your first token.
  • Transparent pagination: every list endpoint has a raw single-page method and an iter_* generator that follows next cursors.
  • Typed errors: HTTP errors map to an exception hierarchy carrying the parsed MURAL error body.

Install

uv add unofficial-mural-python-client

Install the latest development version

To track main (or a specific tag) instead of the latest PyPI release:

uv add git+https://github.com/franpandol/unofficial-mural-python-client

Pin to a tag for reproducible installs, e.g. git+https://github.com/franpandol/unofficial-mural-python-client@v0.1.0.

Contributing / running from source

git clone https://github.com/franpandol/unofficial-mural-python-client
cd unofficial-mural-python-client
uv sync --extra dev

Set up a MURAL OAuth app

  1. Sign in at developers.mural.co and create a new app.
  2. Add the redirect URI http://localhost:8765/callback (used by the local OAuth helper).
  3. Select the scopes you need — least privilege is supported; e.g. a read-only integration only needs murals:read + identity:read.
  4. Copy .env.example to .env and fill in MURAL_CLIENT_ID, MURAL_CLIENT_SECRET, and MURAL_REDIRECT_URI.

If MURAL shows "The redirect URL is invalid", the app dashboard does not allow the exact MURAL_REDIRECT_URI being sent. For the quickstart, add http://localhost:8765/callback exactly: http (not https), localhost (not 127.0.0.1), port 8765, path /callback, and no trailing slash.

If MURAL shows "Required permission scopes not recognized", add every requested scope to that same app's permission-scope allowlist. The quickstart requests identity:read, murals:read, and murals:write; murals:write is required because the smoke test creates widgets and deletes the test mural.

Quickstart

from mural import MuralClient, MuralOAuth, run_local_oauth_flow
from mural.models import CreateMuralRequest, CreateStickyNoteWidget

oauth = MuralOAuth.from_env(scopes=["identity:read", "murals:read", "murals:write"])
token = run_local_oauth_flow(oauth)  # opens the browser, catches the redirect

with MuralClient(token, oauth=oauth) as client:
    me = client.users.me()

    mural = client.murals.create(CreateMuralRequest(room_id=1234567890, title="Retro"))

    client.widgets.create_sticky_notes(
        mural.id,
        [CreateStickyNoteWidget(shape="rectangle", x=100, y=100, text="Went well ✅")],
    )

    for widget in client.widgets.iter_list(mural.id):
        print(widget.type, widget.id)

Or run the end-to-end smoke test (creates and deletes a real mural):

python examples/quickstart.py --room-id <numeric-room-id>

See examples/README.md for the full set of runnable examples and a suggested reading order.

CLI

The package installs a Typer-powered mural command:

uv sync --extra dev
uv run mural --help

Authenticate once with the same OAuth environment variables used by the SDK:

uv run mural auth login \
  --scope identity:read \
  --scope murals:read \
  --scope murals:write

uv run mural auth whoami

Common commands print JSON and reuse the SDK's typed request/response models:

uv run mural workspaces list --limit 10
uv run mural workspaces rooms <workspace-id>
uv run mural rooms create --workspace-id <workspace-id> --name "Project room" --type private
uv run mural murals create --room-id <numeric-room-id> --title "Retro"
uv run mural widgets create-sticky-note <mural-id> --text "Went well" --x 100 --y 100
uv run mural search murals <workspace-id> --query "retro"

By default, OAuth tokens are stored at ~/.config/mural/token.json with private file permissions. Use --token-path <path> on auth and API commands to override it.

Token persistence and auto-refresh

MuralClient refreshes the access token once on a 401 when built with an oauth and a token that has a refresh_token. Persist rotated tokens via the callback:

client = MuralClient(token, oauth=oauth, on_token_refresh=save_token_somewhere)

Pagination

page = client.workspaces.list(limit=25)          # one page: page.value, page.next
for ws in client.workspaces.iter_list():          # all pages, transparently
    print(ws.name)

Errors

from mural import MuralNotFoundError

try:
    client.murals.get("workspace1234.000")
except MuralNotFoundError as error:
    print(error.status_code, error.error.code, error.error.message)

MuralBadRequestError (400), MuralAuthError (401), MuralPermissionError (403), MuralNotFoundError (404), MuralRateLimitError (429), MuralServerError (5xx) — all subclasses of MuralAPIError.

Resource map

Attribute Endpoints
client.murals CRUD, duplicate, export, access info, assets, private mode, chat, tags, timer, users/permissions, visitor settings, voting sessions
client.widgets list/get/delete + typed create/update per type: sticky note*, shape*, textbox*, title*, area, arrow, comment, file, image, table
client.rooms CRUD, folders, murals listing, members, invite/remove
client.workspaces list/get, murals, rooms, templates, invite
client.templates default templates, create-from-mural, delete, create-mural-from-template
client.search murals, rooms, templates within a workspace
client.users /users/me

* bulk endpoints — take a list of bodies, return a list of created widgets, matching the API.

Tests

pytest --cov          # mocked with respx; no network, 98 tests, 99% coverage

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

unofficial_mural_python_client-0.1.0.tar.gz (84.4 kB view details)

Uploaded Source

Built Distribution

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

File details

Details for the file unofficial_mural_python_client-0.1.0.tar.gz.

File metadata

  • Download URL: unofficial_mural_python_client-0.1.0.tar.gz
  • Upload date:
  • Size: 84.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.27 {"installer":{"name":"uv","version":"0.11.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for unofficial_mural_python_client-0.1.0.tar.gz
Algorithm Hash digest
SHA256 5cc6040ac04dd7ed89ed8aae4a46e4ec4a04f60287311a362d31783959ed3fd4
MD5 43c3251a6dfebb3d08538dca8e69203c
BLAKE2b-256 50c62f2dde170b25f8be32edf08d34f00edecbe1de00f7eeaed792cbb781ee78

See more details on using hashes here.

File details

Details for the file unofficial_mural_python_client-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: unofficial_mural_python_client-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 41.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.27 {"installer":{"name":"uv","version":"0.11.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for unofficial_mural_python_client-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c6b7f4713c244b691e93409662c59faef755e7d2c24f7c056492cd419f7055c5
MD5 65922febc3ca5e1e249cad625071ed3c
BLAKE2b-256 6242795baa60f7c1919bd630cd1cc44655c655bfc88ef113f916ac468eeb2254

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