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 followsnextcursors. - 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
- Sign in at developers.mural.co and create a new app.
- Add the redirect URI
http://localhost:8765/callback(used by the local OAuth helper). - Select the scopes you need — least privilege is supported; e.g. a read-only integration
only needs
murals:read+identity:read. - Copy
.env.exampleto.envand fill inMURAL_CLIENT_ID,MURAL_CLIENT_SECRET, andMURAL_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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5cc6040ac04dd7ed89ed8aae4a46e4ec4a04f60287311a362d31783959ed3fd4
|
|
| MD5 |
43c3251a6dfebb3d08538dca8e69203c
|
|
| BLAKE2b-256 |
50c62f2dde170b25f8be32edf08d34f00edecbe1de00f7eeaed792cbb781ee78
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c6b7f4713c244b691e93409662c59faef755e7d2c24f7c056492cd419f7055c5
|
|
| MD5 |
65922febc3ca5e1e249cad625071ed3c
|
|
| BLAKE2b-256 |
6242795baa60f7c1919bd630cd1cc44655c655bfc88ef113f916ac468eeb2254
|