Users

Pass user_id on agents, runs, tasks, memories, and permissions when each end-user needs isolated data. Without it, resources are account-level.

Start with user_id

The same user_id on every call keeps one customer's world separate:

from m8tes import M8tes

client = M8tes()

bot = client.agents.create(name="customer assistant", tools=["gmail"], user_id="cust_123")

run = client.runs.create(agent_id=bot.id, message="summarize open requests",
                         user_id="cust_123", stream=False)
print(client.runs.poll(run.id).output)

task = client.tasks.create(agent_id=bot.id, instructions="daily support summary", user_id="cust_123")
client.memories.create(user_id="cust_123", content="prefers email updates")
client.permissions.create(user_id="cust_123", tool="gmail")

The TypeScript SDK wraps all of these: client.agents, client.runs, client.tasks, client.users, client.memories, and client.permissions.

List endpoints take the same filter, so you only fetch one tenant:

Python
agents = client.agents.list(user_id="cust_123")
runs = client.runs.list(user_id="cust_123")
memories = client.memories.list(user_id="cust_123")
tasks = client.tasks.list(user_id="cust_123")

Usage and billing

Isolation is about data. For billing, any run carrying a user_id settles on your prepaid balance (the API meter), not on your platform plan. See Billing & Usage. You can still see and cap each end-user individually: GET /api/v2/usage/end-users rolls up each end-user's completed runs, cost, and tokens for the current billing period:

Python
for row in client.users.usage().data:
    print(row.user_id, row.runs_used, row.cost_used, row.total_tokens)

alice = client.users.usage("cust_123").data[0]   # one end-user

Cap every end-user account-wide, or throttle one specific end-user with a per-profile override:

client.settings.update(per_end_user_run_limit=50, per_end_user_cost_limit_cents=2000)
client.users.update("cust_123", run_limit=5, rate_per_minute=2)   # override for one user
Cap semantics, error codes, and per-day cost series
  • Per-profile overrides win over the account-wide defaults. run_limit=0 blocks an end-user entirely; None clears an override back to the account default.
  • A capped end-user's next run returns 402 with END_USER_RUN_LIMIT_REACHED or END_USER_COST_LIMIT_REACHED; rate bursts return 429 END_USER_RATE_LIMITED. Other end-users (and your own runs) are unaffected.
  • The rollup endpoint reports the same counters and the EFFECTIVE caps per row, so the numbers always reconcile.
  • For cost attribution over time, client.billing.usage_timeseries(user_id="cust_123") returns that end-user's daily token + USD buckets.

User profiles

Profiles are auto-created the first time you use a new user_id; manage them directly when you want names and emails attached:

Python
user = client.users.create(user_id="cust_456", name="alex lee", email="alex@acme.com", company="acme")

user = client.users.get("cust_456")
for item in client.users.list().data:
    print(item.user_id, item.name)

client.users.update("cust_456", name="alex kim", company="acme inc")
client.users.delete("cust_456")   # removes profile fields only; runs, tasks, memories stay

Strict multi-tenant mode

The classic multi-tenancy bug is forgetting user_id on one call: the data silently lands in the account-level scope, invisible to your end-users. Strict mode rejects those requests instead, and it is on by default for new API accounts:

Python
client.agents.create(name="Bot")
# 422: This account requires user_id on every agent request (strict multi-tenant mode)

client.agents.create(name="Bot", user_id="cust_123")  # scoped: works

Building for just yourself? Turn it off and drop user_id everywhere:

Python
# Personal development only: this disables strict user_id checks account-wide.
# This persists. For customer-facing apps, keep strict mode on and pass user_id.
client.settings.update(require_end_user_id=False)   # or sign up with "require_end_user_id": false
Where strict mode is enforced, and older accounts

Enforced wherever a request scopes data. On writes: agent, task, run, skill and custom-MCP-server creation (a run inheriting scope from its scoped agent passes without an explicit user_id), plus every memory operation. On reads: listing agents, tasks, runs, skills, custom MCP servers, memories or permissions without a user_id is rejected rather than answered with a scope you did not ask for.

Exempt: app connections, replies to pre-existing unscoped runs, catalogue reads (apps, built-in tools; there user_id only flags per-end-user availability), and anything you do in the web console. The console is your own session on your own account, so it shows the cross-end-user view; the rule is about a programmatic call that forgot its scope.

Accounts created before mid-2026 (and platform-product accounts) default to off; opt in with client.settings.update(require_end_user_id=True).

Best practices

  1. Use your own stable internal ID as user_id, and pass it on every relevant request
  2. Do not mix scoped and unscoped writes for the same product flow
  3. Keep permission policies and memories aligned to the same user_id
  4. Keep strict mode on so a forgotten user_id fails loudly instead of writing to the account scope

Next: Runs · Human-in-the-Loop · API Reference

Was this page helpful?