Skip to main content

Python SDK for building autonomous AI agents on m8tes.ai

Project description

m8tes Python SDK

PyPI Python 3.11+ License: MIT

The agent infrastructure API for building autonomous AI teammates. Deploy teammates that connect to 70+ apps, run on schedules, have memory, and work autonomously.

Install

pip install m8tes

Quick start

from m8tes import M8tes

client = M8tes()  # uses M8TES_API_KEY env var

run = client.runs.create(
    name="support triage",
    instructions="triage inbound support emails. create Linear tickets "
                 "for bugs. escalate urgent issues to #support-escalations.",
    tools=["gmail", "linear", "slack"],
    message="process all unread support emails from today",
    stream=False,
)
run = client.runs.poll(run.id)
print(run.output)

Why m8tes

  • Hosted runtime — sandboxed execution with real-time streaming. No servers to manage.
  • 70+ managed integrations — Gmail, Slack, Notion, HubSpot, Stripe, Linear, Google Ads. OAuth handled for you.
  • Triggers — run teammates on demand, on a schedule, from a webhook, or by email.
  • Human-in-the-loop — teammates ask for approval before taking sensitive actions.
  • Agent memory — persistent context that builds over time. Per-user scoping for multi-tenant apps.
  • File handling — teammates generate reports, spreadsheets, and exports you can download through the API.
  • Multi-tenant by default — isolated memory, tools, and permissions per end-user.

Runs

Streaming (default)

for event in client.runs.create(
    message="pull MRR from Stripe, compare to last month, post the delta to #revenue",
    tools=["stripe", "slack"],
):
    match event.type:
        case "text-delta":      print(event.delta, end="")
        case "tool-call-start": print(f"\n  {event.tool_name}")
        case "tool-result-end": print(f"  > {event.result[:100]}")
        case "done":            print(f"\n  {event.stop_reason}")

Non-streaming

run = client.runs.create(message="generate quarterly report", stream=False)
result = client.runs.poll(run.id)  # blocks until complete
print(result.output)

# or use the convenience wrapper
result = client.runs.create_and_wait(message="generate quarterly report")

Context manager

with client.runs.create(message="summarize inbox") as stream:
    for event in stream:
        print(event.type)
print(stream.text)  # full accumulated text

Reply to a run

for event in client.runs.reply(run.id, message="also break it down by region"):
    print(event.type, event.raw)

# or block until complete
result = client.runs.reply_and_wait(run.id, message="also break it down by region")

Stream text only

for chunk in client.runs.stream_text(message="summarize inbox"):
    print(chunk, end="")

Human-in-the-loop

run = client.runs.create(
    message="draft and send the weekly report",
    human_in_the_loop=True,
    permission_mode="approval",  # or "plan", "autonomous"
    stream=False,
)

# check pending permission requests
pending = client.runs.permissions(run.id)

# approve a tool use
client.runs.approve(run.id, request_id="req_123", decision="allow")

# answer an agent question
client.runs.answer(run.id, answers={"Which channel?": "#general"})

Triggers

# schedule -- every weekday at 9am
client.tasks.triggers.create(task.id, type="schedule", cron="0 9 * * 1-5")

# webhook -- POST to a URL to trigger runs
trigger = client.tasks.triggers.create(task.id, type="webhook")
print(trigger.url)  # POST here to trigger

# email -- forward emails to trigger runs
trigger = client.tasks.triggers.create(task.id, type="email")
print(trigger.address)  # forward emails here

# on demand -- run a saved task directly
for event in client.tasks.run(task.id):
    print(event.type, event.raw)

Multi-tenancy

Give each user their own AI teammate with isolated memory, tools, and permissions.

# create a user profile
client.users.create(user_id="cust_123", name="Acme Corp", email="admin@acme.com")

# give them their own teammate
bot = client.teammates.create(
    name="acme assistant",
    tools=["gmail", "slack"],
    user_id="cust_123",
)

# seed their memory
client.memories.create(user_id="cust_123", content="prefers email over slack")

# pre-approve tools
client.permissions.create(user_id="cust_123", tool="gmail")

# run on their behalf -- memory, permissions, history all scoped
run = client.runs.create_and_wait(
    teammate_id=bot.id,
    message="check inbox for urgent items",
    user_id="cust_123",
)

Resources

Resource Key methods Description
client.teammates create list get update delete enable_webhook enable_email_inbox Agent personas with tools and instructions
client.runs create poll create_and_wait reply reply_and_wait stream_text get list cancel approve answer list_files download_file Execute teammates and stream results
client.tasks create list get update delete run Reusable task definitions
client.tasks.triggers create list delete Schedule, webhook, and email triggers
client.apps list connect connect_complete disconnect Tool catalog and OAuth connections
client.memories create list delete Per-user persistent memory
client.permissions create list delete Pre-approve tools for end-users
client.users create list get update delete End-user profile management
client.webhooks create list get update delete list_deliveries verify_signature Webhook endpoints and delivery tracking
client.settings get update Account configuration

Pagination

# standard page
page = client.runs.list(limit=50)
for run in page.data:
    print(run.id, run.status)

# auto-paginate through all results
for run in client.runs.list().auto_paging_iter():
    print(run.id, run.status)

Webhooks

# register an endpoint
hook = client.webhooks.create(
    url="https://example.com/hook",
    events=["run.completed", "run.failed"],
)
secret = hook.secret  # save this -- only shown once

# verify incoming webhooks (e.g. in Flask/FastAPI)
from m8tes import Webhooks

is_valid = Webhooks.verify_signature(
    body=request.body,
    headers=dict(request.headers),
    secret=secret,
)

Files

files = client.runs.list_files(run_id=42)
for f in files:
    print(f.name, f.size)

content = client.runs.download_file(run_id=42, filename="report.csv")

Error handling

from m8tes import M8tes, NotFoundError, RateLimitError, AuthenticationError

try:
    client.teammates.get(999)
except NotFoundError:
    print("teammate not found")
except RateLimitError as e:
    print(f"rate limited, retry after {e.retry_after}s")
except AuthenticationError:
    print("invalid API key")

Configuration

Variable Description Default
M8TES_API_KEY API key for authentication
M8TES_BASE_URL API endpoint https://m8tes.ai
client = M8tes(api_key="m8_...", timeout=300)  # custom timeout in seconds

CLI

m8tes auth login                    # authenticate
m8tes mate task ID "message"        # run a task
m8tes mate chat ID                  # interactive chat

See CLI documentation for all commands and options.

License

MIT License — see LICENSE for details.

Links

Free to start. No infrastructure to manage. Start building.

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

m8tes-0.2.0.tar.gz (96.3 kB view details)

Uploaded Source

Built Distribution

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

m8tes-0.2.0-py3-none-any.whl (121.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: m8tes-0.2.0.tar.gz
  • Upload date:
  • Size: 96.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for m8tes-0.2.0.tar.gz
Algorithm Hash digest
SHA256 7c6dda07fbcacf121f22d03412c3015cd68ab62592bc3fe9ba40c0e525f9b094
MD5 bfd44924451a9ee351b29a4a707f84ef
BLAKE2b-256 63efc2955ce7ec3de621b92b504949b434b6f4d31b71d40db8cc84260de22b7f

See more details on using hashes here.

File details

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

File metadata

  • Download URL: m8tes-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 121.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for m8tes-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e402d59b14149dba4dab9caba1b6a635bead213ce0abacd19ab726388b7788ce
MD5 83090d8e87108bbb73e9345a85862ffe
BLAKE2b-256 59930debafada92c40b6da7bfcaa81ac89d1e3c30d75294c9dfd48ed8231d9ed

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