Skip to content

Documentation navigation

Authentication & API Keys

Create, use, and rotate project API keys for the SDK.

#Authentication & API Keys

Every request the mAIvn SDK makes on your behalf is tied to an API key. The key tells the hosted orchestrator who you are, which project the work belongs to, and what that key is allowed to do. This guide shows how to authenticate with the SDK, how key scopes shape what you can call, what a rejected key looks like in Python, and how to keep keys out of harm's way.

#What This Is and Why It Matters

When you build with mAIvn, your tool code runs locally, but the planning happens on a hosted orchestrator. To reach that orchestrator, the SDK needs to prove the request is yours. That proof is an API key: a secret string you create once in the mAIvn Developer Portal and hand to the SDK.

Think of it like the key to a building. The SDK presents it on every request, and the server checks that it is valid and that it has permission for what you're asking. A good key gets you in and lets you do your job. A missing, expired, or under-privileged key gets you turned away, and the SDK turns that "no" into a clear Python exception you can catch.

You never have to assemble auth headers or manage tokens by hand. You give the SDK a key, and it does the rest.

#How the SDK Authenticates

Credentials can come from a constructor, terminal environment, an explicitly selected dotenv file, or a single-line key file. Existing explicit keys and shared clients continue to work.

#Use terminal environment variables

Export MAIVN_API_KEY before starting Python, then construct the SDK objects without repeating the key:

Python
from maivn import Agent, Client, Swarm

agent = Agent(name="assistant")
client = Client()
team = Swarm(name="team", agents=[agent])

An empty swarm can be declared without credentials and resolves its client when first used. A swarm with members inherits the first member's client unless you supply a client or credential source explicitly.

#Select a credential file

Python
from maivn import Agent, Client, Swarm

client = Client(env_file="/path/to/project/.env")
agent = Agent(name="assistant", api_key_file="/path/to/secrets/maivn-key")
team = Swarm(name="team", env_file="/path/to/project/.env")

The key file contains only the API key on one line. You can also set MAIVN_API_KEY_FILE in the terminal or selected dotenv file. Relative paths resolve from the process working directory. No shell PATH or parent-directory search is performed.

Credential priority is:

  1. Explicit api_key=....
  2. MAIVN_API_KEY in the process environment.
  3. MAIVN_API_KEY in the selected env_file.
  4. api_key_file=..., or the file named by MAIVN_API_KEY_FILE.

An explicit key-file path takes precedence over a key-file path from the environment. Key values from the higher-priority sources still win. The selected dotenv file also supplies SDK endpoint and project settings, with terminal values taking precedence. It does not modify os.environ.

If you already call load_dotenv(path, override=False), keep doing so: the SDK uses the environment that call populates. The SDK itself only loads a dotenv file when you pass env_file. Missing or unreadable selected files produce configuration errors; key-file contents are never included in those errors.

Keep credential files outside version control and restrict them to the account running the application. SDK object representations hide API-key fields. Explicitly dumping configuration still contains the credential and must not be logged.

The compatibility builders ClientBuilder.from_environment(...) and ConfigurationBuilder.from_environment(...) use the same source resolver and accept env_file and api_key_file. A built configuration retains endpoint, project, timezone, thread, and timeout settings when converted back to a client; nested configuration representations also hide the key. See Configuration for the complete round-trip example.

#Supply a key or share a client

Python
from maivn import Agent, Client

client = Client(api_key="your-runtime-key")
researcher = Agent(name="researcher", client=client)
writer = Agent(name="writer", client=client)

An injected client remains authoritative. Scope credential options do not replace it. Likewise, an explicit ClientConfig supplies the client's credentials instead of file loading. Share a client explicitly when several agents should use the same configuration.

If no credential source is available, Client raises ConfigurationError. Agent preserves its existing ValueError for missing credentials. An explicitly selected unreadable file raises ConfigurationError.

#What travels on the wire

The SDK sends the key as an Authorization: Bearer ... header. The client handles this header; callers supply a credential source instead of building authentication headers themselves.

#Key Scopes From the SDK Consumer's Point of View

API keys are restricted to the project where they were created. The Portal does not offer enforced read-only, read-write, or administrator permission levels for these keys. Do not rely on those labels to constrain a runtime credential.

Use separate projects and keys for workloads that need separate access or revocation. Server authorization also checks the operation and authenticated account context. A valid project key does not grant arbitrary cross-project access or administrative privileges.

The public SDK has no account-level billing retrieval method. Its billing classes are read models only; use the Portal for supported billing operations. See Billing models.

#Don't confuse key scope with tool permissions

There is a separate, finer-grained concept inside the SDK: the @toolify(permissions=...) declaration, which lets a tool advertise the categories of work it performs (read, write, delete, and so on) so your host can enforce least-privilege at the tool layer. That is about what an individual tool is allowed to do once a request is already authenticated. It is not the same thing as the API key's account scope. See Decorators for PermissionFlag / PermissionSet.

#What Happens on a Rejected or Insufficient Key

Authentication failures surface as Python exceptions, not silent empty responses. Missing credentials raise ValueError on Agent and ConfigurationError on Client; typed API rejections use MaivnHTTPError.

Python
from maivn import Agent, MaivnHTTPError, MaivnSDKError
from maivn.messages import HumanMessage

agent = Agent(name='assistant', api_key='definitely-not-valid')

try:
    response = agent.invoke([HumanMessage(content='Hello')])
except MaivnHTTPError as exc:
    print('Request failed:', exc.status_code, exc.code, exc.reason)
except MaivnSDKError as exc:
    print('SDK failure:', str(exc))
  • A 401 means your key is missing or invalid. There is nothing to retry until you supply a valid key.
  • A 403 means your key is valid but not permitted for what you asked. Check the project boundary and the operation's authorization requirements. Retrying unchanged credentials will not grant permission.

MaivnHTTPError exposes status_code, reason, optional code, and optional detail. It does not expose to_dict(), with_context(), or an automatic retry classifier.

Other request rejections also use MaivnHTTPError. Inspect the status and service code rather than parsing the exception string:

Python
try:
    response = agent.invoke([HumanMessage(content='...')])
except MaivnHTTPError as exc:
    print('Rejected:', exc.status_code, exc.reason)

Neither authentication nor deterministic validation rejections should be blindly retried. See Errors & Exceptions for the current public hierarchy and transport exceptions.

#Where Keys Come From and Rotating Them

API keys are created, listed, and revoked in the mAIvn Developer Portal, scoped to a project. The portal page is the canonical reference for that workflow. See API Keys and Webhooks for the create/copy/store/revoke mechanics. This section covers only what matters once a key is in the SDK's hands.

A key is bound to a project within an organization. Use that project's key for its runtime operations; the key does not grant access to another project.

Rotating without downtime is straightforward because authentication is just a string the SDK reads at startup. Create the new key, deploy it to your secret store, restart or re-instantiate your clients so they pick up the new value, confirm traffic is flowing, then revoke the old key. Revocation takes effect everywhere within 30 seconds. Since a Client reads the key when it is constructed, the cleanest rotation is to build a fresh Client (or restart the process) with the new key rather than mutating a live one.

#Security Best Practices

Read keys from the environment; never hard-code them. A literal key in source ends up in version control, logs, and screenshots. Read it at startup instead.

Python
import os

from maivn import Client

client = Client(api_key=os.environ['MAIVN_API_KEY'])

Never ship keys to the frontend. API keys are server-to-server credentials. A browser bundle is public; a key placed there is a leaked key. Keep agent invocation on your backend and expose only the results (or a streamed event feed) to clients. When you stream progress to a browser, mount the events adapter on your own application (mount_events(app) wires up GET /maivn/events/{session_id} on your server) so the browser talks to your route, never to the orchestrator with your key. See Frontend Events.

Use private_data for in-tool secrets, not the API key. Your mAIvn API key authenticates the SDK. It is not the place to stash the third-party credentials your tools need (a payment provider key, a database password, a customer's PII). Those belong in private_data, which keeps the real values out of the model's prompt and injects them into your tool only at execution time.

Python
from maivn import Agent, depends_on_private_data

agent = Agent(name='billing_agent', api_key=os.environ['MAIVN_API_KEY'])
agent.private_data = {'stripe_api_key': os.environ['STRIPE_API_KEY']}

@agent.toolify(description='Charge a customer')
@depends_on_private_data(data_key='stripe_api_key', arg_name='key')
def charge(amount: int, key: str) -> dict:
    # 'key' holds the real secret at runtime; the model never sees it.
    return {'charged': amount}

The model plans against the name stripe_api_key, never its value, and the runtime redacts known private values from tool results before they return to the model. See the Private Data Guide for the full model.

Separate keys by workload and project. Rotate keys on a schedule and after an incident. See Key scopes and Rotating them for the steps. The Portal's security checklist also covers webhooks.

#Next Steps

  • Overview — what mAIvn is and the one mental model behind it.
  • Core Concepts — the vocabulary (scopes, tools, responses) the guides build on.
  • Quickstart — set a key and run your first agent end to end.
  • Client — the Client and ClientBuilder.from_environment() reference.
  • API Keys and Webhooks — create, list, and revoke keys (and webhooks) in the portal.
  • Portal Authentication — signing in to the portal itself: login, sessions, and password reset.
  • Errors & Exceptions — the full SDK error catalog beyond the auth-specific cases here.
  • Billing models — aggregated usage read models and the current transport boundary.
  • Private Data and PII Protection — keep in-tool secrets and user PII out of the model.
  • Frontend Events — stream progress to a browser through your own route, not the orchestrator.