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:
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:
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-userCap 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 userCap semantics, error codes, and per-day cost series
- Per-profile overrides win over the account-wide defaults.
run_limit=0blocks an end-user entirely;Noneclears an override back to the account default. - A capped end-user's next run returns
402withEND_USER_RUN_LIMIT_REACHEDorEND_USER_COST_LIMIT_REACHED; rate bursts return429 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:
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 stayStrict 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:
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: worksBuilding for just yourself? Turn it off and drop user_id everywhere:
# 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": falseWhere 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
- Use your own stable internal ID as
user_id, and pass it on every relevant request - Do not mix scoped and unscoped writes for the same product flow
- Keep permission policies and memories aligned to the same
user_id - Keep strict mode on so a forgotten
user_idfails loudly instead of writing to the account scope
Next: Runs · Human-in-the-Loop · API Reference