Skip to main content

Maton Python SDK

Official Python SDK for Maton — connect and automate 150+ apps (Gmail, Slack, GitHub, Notion, HubSpot, Airtable, and more) from Python.

Install

pip install maton-ai
# or
uv add maton-ai
# or
poetry add maton-ai

Quickstart

Sign in once through the browser, then construct the client with no arguments:

import maton_ai

maton_ai.login()  # opens a browser; stores the session locally
from maton_ai import Maton

maton = Maton()

conn = maton.connections.create(app="gmail")
connection_id = conn["id"]

gmail = maton.google_mail(connection=connection_id)
messages = gmail.messages.list(q="is:unread", max_results=10)
gmail.messages.send(to="alice@example.com", subject="hi", body="hello")

Authentication

maton_ai.login() runs an OAuth flow in your browser and stores the session locally. Nothing else is required: the SDK signs in, renews, and signs out on its own, with no other Maton tool involved.

maton_ai.login()  # sign in, and make it the active session
maton_ai.login(make_active=False)  # add a session without switching to it
maton_ai.logout()  # revoke and clear the active session
maton_ai.logout("alice@example.com")  # sign one account out

When the authorization server refuses, login() raises maton_ai.OAuthError — a MatonError subclass that carries the server's own error code as .code, so access_denied (the user declined) is distinguishable from a misconfigured client. Everything else that can go wrong on the way — discovery, a timeout waiting for the browser — raises MatonError. logout() clears the local session even when the revocation call fails, so a machine can always be signed out.

Credentials resolve in this order:

  1. Maton(profile="...")
  2. Maton(api_key="...")
  3. the MATON_API_KEY environment variable
  4. MATON_PROFILE
  5. the active stored session, or the sole one when none is marked active

Explicit credentials always win over ambient machine state. Passing both profile= and api_key= is rejected as ambiguous, and a named profile that cannot be resolved raises rather than quietly authenticating as another account.

One deliberate difference from the Maton CLI. In the CLI, MATON_API_KEY is a global override that outranks even -p/--profile. In this SDK an explicit profile= argument outranks MATON_API_KEY, because an argument written in code is a stronger signal of intent than an exported variable, and because a multi-tenant process must be able to select an account on a machine that happens to have MATON_API_KEY set. MATON_PROFILE does not outrank MATON_API_KEY — between two ambient sources, the CLI's order is kept.

maton = Maton(profile="alice@example.com")  # a specific account

An API key remains the right choice where a browser sign-in is impossible, such as CI:

maton = Maton(api_key=os.environ["MATON_API_KEY"])

Where the session is stored

Session metadata goes to the python section of credentials.json in ~/.config/maton on macOS and Linux, or %AppData%\Maton on Windows. The file is shared with the other Maton SDKs, while each SDK keeps independent profiles and an independent active-profile selection. $MATON_CONFIG_DIR overrides the directory outright, and $XDG_CONFIG_HOME/maton takes precedence over the defaults. The tokens themselves go to the OS keyring when the optional extra is installed:

pip install "maton-ai[keyring]"

Without it, tokens are written to credentials.json in plaintext at mode 0600, and login() warns that it did so. Installing the extra is recommended on any machine where that file is backed up or synced.

Access tokens are short-lived; the SDK renews them in-process from the stored refresh token, so a long-running client keeps working without re-authenticating.

The same pattern works for every supported app:

slack = maton.slack(connection=slack_conn_id)
slack.messages.send(channel="#general", text="deploy finished ✅")

gh = maton.github(connection=gh_conn_id)
gh.issues.create(repo="maton-ai/maton-py", title="bug: ...", body="...")

notion = maton.notion(connection=notion_conn_id)
notion.data_sources.query(data_source_id="...", filter={"property": "Status", "status": {"equals": "Done"}})

hubspot = maton.hubspot(connection=hubspot_conn_id)
hubspot.contacts.list(limit=25)

There are three places to select a connection, in order of precedence (per-call beats accessor beats constructor):

maton = Maton(api_key=..., connection=connection_id)

gmail = maton.google_mail(connection=connection_id)
gmail.messages.list(q="is:unread")

maton.google_mail.messages.list(q="is:unread", connection=connection_id)

Generic passthrough:

maton.api.post(
    "google-mail",
    "/gmail/v1/users/me/messages/send",
    json={"raw": "..."},
    connection=connection_id,
)

Functions

A function is a Python or Node handler that Maton hosts. Code lives in immutable versions; the active version answers requests on the function's own host, at the url that get returns. Manage them through maton.functions, with maton.functions.versions, maton.functions.env, and maton.functions.runs sub-resources:

fn = maton.functions.create(
    name="greet",
    runtime="python3.12",
    files={"main.py": "def handler(event):\n    return {'hello': 'ada'}\n"},
)
function_id = fn["function_id"]

# ``MATON_API_KEY`` is runtime-provided.
maton.functions.env.create(
    function_id,
    env=[{"key": "API_BASE", "value": "https://example.com", "type": "PLAIN"}],
)
maton.functions.env.update(function_id, "API_BASE", "https://example.org")

url = maton.functions.get(function_id)["url"]
try:
    resp = maton.api.with_raw_response.post(url, json={"name": "ada"})
    print(resp.function_run_id, resp.json())
except MatonError as exc:
    for event in maton.functions.runs.logs.tail(function_id, exc.function_run_id):
        print(event["message"], end="")

maton.functions.versions.list(function_id)
maton.functions.update(function_id, version=1)
maton.functions.update(
    function_id,
    files={
        "main.py": "import json\ndef handler(event):\n    body = json.loads(event.get('body') or '{}')\n    return {'hi': body.get('name')}\n"
    },
)
maton.functions.code.download(function_id)
maton.functions.search('"hello"', context=2)

Handler

The runtime calls the handler with event and an optional context, and turns its return value into an HTTP response.

Event

{
  "version": 1,
  "rawPath": "/",
  "rawQueryString": "a=1",
  "cookies": ["k=v"],
  "headers": { "host": "greet-a1b2c3.maton.app" },
  "queryStringParameters": { "a": "1" },
  "requestContext": {
    "accountId": "...",
    "domainName": "greet-a1b2c3.maton.app",
    "domainPrefix": "greet-a1b2c3",
    "http": {
      "method": "POST",
      "path": "/",
      "protocol": "HTTP/1.1",
      "sourceIp": "...",
      "userAgent": "..."
    },
    "runId": "...",
    "time": "30/Aug/2026:17:24:03 +0000",
    "timeEpoch": 1788000000000
  },
  "body": "{\"name\":\"ada\"}",
  "isBase64Encoded": false
}

Context (optional)

Python

context.run_id  # "..."
context.function_name  # "greet"
context.function_version  # "1"
context.function_id  # "..."
context.account_id  # "..."
context.memory_limit_in_mb  # 128

Node

{
  "runId": "...",
  "functionName": "greet",
  "functionVersion": "1",
  "functionId": "...",
  "accountId": "...",
  "memoryLimitInMB": "128"
}

Response

Anything the handler returns that is not a dict carrying a statusCode key is sent as the response body with a 200. A returned string is JSON-encoded, so return "hello" comes back as "hello" with the quotes. To set the status or headers, return an envelope carrying statusCode instead:

def handler(event):
    return {
        "statusCode": 201,
        "headers": {"content-type": "text/plain"},
        "body": "created",
    }

Triggers

Triggers register an event source (e.g. GitHub pull_request.opened) and fan matching events out to webhook destinations. Manage them through maton.triggers, with maton.triggers.destinations and maton.triggers.events sub-resources:

trigger = maton.triggers.create(
    source="github",
    event_type="pull_request.opened",
    connection_id=gh_conn_id,
    parameters={"repo": "maton-ai/cli"},
    destinations=[{"url": "https://example.com/hook"}],
)
trigger_id = trigger["trigger"]["trigger_id"]

maton.triggers.list(source="github", status="ENABLED")
maton.triggers.update(trigger_id, status="DISABLED")

dst = maton.triggers.destinations.create(trigger_id, url="https://example.com/hook")
maton.triggers.destinations.rotate_secret(trigger_id, dst["destination"]["destination_id"])

events = maton.triggers.events.list(trigger_id, limit=20)
maton.triggers.events.replay(trigger_id, events["events"][0]["event_id"])

for event in maton.triggers.events.watch(trigger_id):
    handle(event)

Supported apps

App Accessor Highlights
Asana maton.asana projects, tasks, workspaces
GitHub maton.github repos, issues, PRs, releases, labels
Google Ads maton.google_ads accounts, campaigns, ad groups, ads, keywords
Google Calendar maton.google_calendar calendars, events, ACL, freebusy
Google Docs maton.google_docs documents (create / get / write)
Google Drive maton.google_drive files, drives, permissions, comments, revisions
Google Mail maton.google_mail drafts, labels, messages, threads
Google Sheets maton.google_sheets spreadsheets, sheets, values
Google Tasks maton.google_tasks tasklists, tasks
HubSpot maton.hubspot contacts, companies, deals, associations
Jira maton.jira issues, projects, transitions, comments, users
Linear maton.linear issues, projects, cycles, teams (GraphQL)
Microsoft Teams maton.microsoft_teams teams, channels, chats, messages, meetings
Notion maton.notion pages, databases, data sources, blocks, search
OneDrive maton.one_drive drives, items (upload, share, move)
Outlook maton.outlook messages, events, contacts, folders
Salesforce maton.salesforce records, query, search, composites
Slack maton.slack channels, messages, files, reactions, schedules
Stripe maton.stripe customers, charges, invoices, subscriptions
Trello maton.trello boards, cards, lists, checklists, labels
YouTube maton.youtube channels, videos, playlists, comments, search

Reliability

Every call goes through an httpx-based client with automatic retries. The defaults are configurable on the constructor:

maton = Maton(
    api_key=...,
    timeout=30.0,  # per-request timeout, seconds
    max_retries=2,  # retry attempts on transient failures
    max_backoff=20.0,  # cap on a single backoff sleep, seconds
)

Retries fire on connection errors and on 429 / 500 / 502 / 503 / 504; other 4xx/5xx surface immediately. Backoff is truncated exponential with full jitter (botocore standard mode), and honors a server-provided Retry-After header.

Errors

All failures raise a subclass of MatonError, each carrying status_code, request_id, the parsed body, and function_run_id — set only when the failing response carried X-Function-Run-Id, in practice a function invocation:

Exception When
AccessDeniedError 401/403 — bad/missing API key or insufficient scope
ResourceNotFoundError 404
TooManyRequestsError 429 — also exposes .retry_after
ValidationError other 4xx (incl. 412 when an action needs a connection)
InternalServerError 5xx or unexpected upstream response
APIConnectionError network/transport failure (couldn't reach the gateway)

A handful of apps wrap their vendor-specific error envelopes (e.g. a GraphQL errors array or a Slack ok: false payload) in a dedicated subclass, so you can catch them by app while still falling back to MatonError:

Exception App
GitHubError GitHub GraphQL/API errors
GoogleDriveError Google Drive API errors
LinearError Linear GraphQL/API errors
SlackError Slack API errors
StripeError Stripe API errors
from maton_ai import MatonError, TooManyRequestsError

try:
    maton.google_mail.messages.list(q="is:unread", connection=connection_id)
except TooManyRequestsError as exc:
    print("slow down; retry after", exc.retry_after)
except MatonError as exc:
    print(exc.status_code, exc.request_id, exc.body)

Download files

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

Source Distribution

maton_ai-0.3.0.tar.gz (132.5 kB view details)

Uploaded Source

Built Distribution

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

maton_ai-0.3.0-py3-none-any.whl (155.1 kB view details)

Uploaded Python 3

File details

Details for the file maton_ai-0.3.0.tar.gz.

File metadata

  • Download URL: maton_ai-0.3.0.tar.gz
  • Upload date:
  • Size: 132.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for maton_ai-0.3.0.tar.gz
Algorithm Hash digest
SHA256 20464dd27b27f30ea2489d5ae4a2f1693cdaec7c4c7bcee0a51d0c84c92e530b
MD5 4cfe3415c7df0d84cb61c2c7ae355977
BLAKE2b-256 580866d981cbbc25104a97b37572a205100ed199d28b8186eb222546ed114c9c

See more details on using hashes here.

Provenance

The following attestation bundles were made for maton_ai-0.3.0.tar.gz:

Publisher: release-pypi.yml on maton-ai/maton-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file maton_ai-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: maton_ai-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 155.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for maton_ai-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 19a482f52e8fd2cc9bbbb66904c69876488b62d37f4036856f7d4a426234c4ea
MD5 d19cf71ed8f668ac333a800b087feb6e
BLAKE2b-256 81498d4ca9ed6354b205e127bd2d12b06a062782adfd2f19ade13c86e74d9199

See more details on using hashes here.

Provenance

The following attestation bundles were made for maton_ai-0.3.0-py3-none-any.whl:

Publisher: release-pypi.yml on maton-ai/maton-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.3.1

2 files

This release

0.3.0 This release

2 files

0.2.0

2 files

0.1.0

2 files

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