Environment Variables

Configuration reference for the Band Python SDK

The Band SDK uses two configuration files: .env for environment variables and agent_config.yaml for agent credentials.

Neither file is loaded automatically. load_dotenv() from the python-dotenv package reads .env into the process environment, and load_agent_config() reads agent_config.yaml. The SDK reads two groups of variables out of the process environment itself, the platform URLs and the BAND_LOG_* logging knobs, so call load_dotenv() before you construct an Agent or configure logging. Everything else in .env, including provider API keys, reaches its consumer only because that library reads it. The bundled command-line tools read BAND_* variables directly. See Command-Line Tools.

For initial setup and installation, see the Setup tutorial. This page is a complete reference for all configuration options.


Configuration Files

FilePurposeContains
.envEnvironment variablesPlatform URLs, LLM provider API keys
agent_config.yamlAgent credentialsAgent ID and API key per agent

Neither file ships in the installed package. .env.example and agent_config.yaml.example exist only in a clone of the SDK’s git repository, so cp .env.example .env fails in a project created with uv add band-sdk. Create both files yourself, with the contents shown in the sections below.

Add both files to .gitignore before your first commit. See Security.


Platform Connection

Two variables carry the platform URLs. Agent.create() resolves both itself: omit ws_url or rest_url, or pass None, and the SDK reads BAND_WS_URL / BAND_REST_URL, falling back to the Band Cloud URL when the variable is unset or empty.

VariableValue for Band CloudConstructor argument
BAND_REST_URLhttps://app.band.airest_url
BAND_WS_URLwss://app.band.ai/api/v1/socket/websocketws_url
.env
$BAND_REST_URL=https://app.band.ai
$BAND_WS_URL=wss://app.band.ai/api/v1/socket/websocket

Change the values only when connecting to a different environment, such as a self-hosted deployment.

An explicit argument always wins over the environment, so the tutorials keep passing ws_url=os.getenv("BAND_WS_URL") and rest_url=os.getenv("BAND_REST_URL"). That is still correct, and safe when the variable is unset, because None resolves the same way an omitted argument does. Writing the platform URL into the call is what makes a snippet self-describing.

load_dotenv() has to run before Agent.create(). The SDK reads the process environment, not the file, so a .env loaded afterwards has no effect on a connection already built.

Set both variables or neither. Each falls back independently, so setting only one points REST and WebSocket at different platforms, and nothing reports it: your credentials authenticate against one environment while messages stream from the other.


Agent Credentials

Agent credentials go in agent_config.yaml, not in environment variables. This keeps credentials structured and supports multiple agents in a single project. load_agent_config() looks for the file in the current working directory, so run your agent from the directory that holds it.

agent_config.yaml
1my_agent:
2 agent_id: "your-agent-uuid"
3 api_key: "your-api-key"
4
5another_agent:
6 agent_id: "another-agent-uuid"
7 api_key: "another-api-key"

Load credentials in your code:

1from band.config import load_agent_config
2
3agent_id, api_key = load_agent_config("my_agent")

The key name (my_agent) matches the top-level key in the YAML file. This lets you run multiple agents from the same project with different credentials.


LLM Provider Keys

Add your LLM provider API keys to .env. Band never reads these; the provider client libraries read them from the environment themselves, once load_dotenv() has run.

VariableProviderRequired
OPENAI_API_KEYOpenAI (GPT-4, GPT-4o)If using OpenAI models
ANTHROPIC_API_KEYAnthropic (Claude)If using Anthropic models
.env
$OPENAI_API_KEY=sk-your-openai-key-here
$ANTHROPIC_API_KEY=sk-ant-your-anthropic-key-here

Set only the keys for the providers you use. No key is required unless the model you configure needs it.


Framework-Specific Variables

Some LLM frameworks use their own environment variables:

VariableFrameworkPurpose
LANGCHAIN_API_KEYLangChain / LangGraphLangSmith tracing
LANGCHAIN_TRACING_V2LangChain / LangGraphEnable tracing (true)
LANGCHAIN_PROJECTLangChain / LangGraphProject name for LangSmith

LangChain and LangSmith read these, not Band. They are optional and only needed for framework features like tracing.


Command-Line Tools

The three console scripts the package installs read these variables directly. For band-trigger and band-acp each variable supplies the default for a matching CLI flag.

VariableToolPurpose
BAND_API_KEYband-trigger, band-acpAPI key. Preferred over --api-key, which exposes the key in process listings
BAND_AGENT_IDband-acpAgent UUID
BAND_REST_URLband-trigger, band-acpREST API URL
BAND_WS_URLband-acpWebSocket URL
BAND_AUTH_MODEband-triggeragent or user, default agent
BAND_TARGET_HANDLEband-triggerTarget agent handle, for example @owner/agent-name
BAND_MESSAGEband-triggerMessage to send
BAND_TRIGGER_TIMEOUTband-triggerTimeout in seconds for the whole operation, default 120
BAND_AGENT_KEYband-room-viewAgent API key for the Claude Desktop room view; normally read from the band-mcp server entry in the Desktop config
BAND_DESKTOP_CONFIGband-room-viewPath to the Claude Desktop config file to read the band-mcp server from
BAND_DESKTOP_MCP_SERVERband-room-viewName of the MCP server entry to use when the config declares more than one

Band SDK Logging

Band logging is opt-in. Two entry points apply the same one process-wide configuration: configure_logging() takes it as arguments, and LogSettings reads and validates it from the BAND_LOG_* environment variables.

From arguments

For an embedded SDK, show Band logs while leaving unrelated application loggers at WARNING:

1from band import configure_logging
2
3configure_logging()

For an agent, runner, or CLI that owns the process, also show that process’s own loggers:

1from band import configure_logging
2
3configure_logging(root_level="INFO")

root_level applies to all non-Band loggers, not just the process’s own, so third-party dependencies (e.g. httpx) become as verbose as the level you pass. Use extra_loggers to pin individual dependencies back down, or chatty_logger_levels() for the Band HTTP and WebSocket dependencies as a group.

ParameterDefaultDescription
levelINFOLevel for the band logger, as a logging constant or a name such as "DEBUG"
root_levelWARNINGLevel for non-Band loggers
stylestandardConsole style: standard, rich, or json
streamstderrConsole stream: stderr or stdout
datefmt%Y-%m-%d %H:%M:%STimestamp format
fmtNoneCustom format string for the standard console and file formatters; ignored by rich and json
fmt_style%Format string style for fmt: %, {, or $
extra_loggersNoneLogger name to level mapping, for example {"httpx": "WARNING"}
json_fieldsNoneLogRecord field names to include in json output
static_fieldsNoneFixed fields added to every json record
log_fileNonePath for a second, file handler; None disables file logging
max_bytes0Maximum file size before rotation; 0 uses a plain FileHandler
backup_count1Rotated files to keep when max_bytes is positive; must be at least 1 there
file_styleNoneFile formatter style: standard or json; defaults to standard when log_file is set
file_levelNoneLevel for the file handler; defaults to level

The rich and json styles require the optional logging extra:

$uv add "band-sdk[logging]"

From the environment

LogSettings maps the same configuration onto environment variables. Construct it and call configure():

1from band import LogSettings
2
3LogSettings().configure()

For an agent, runner, or CLI that owns the process, for_application() raises the process’s own loggers to the Band level first:

1from band import LogSettings
2
3LogSettings().for_application().configure()

for_application() raises the level for all non-Band loggers, not just the process’s own, so third-party dependencies also become as verbose as BAND_LOG_LEVEL. It is a no-op when BAND_LOG_ROOT_LEVEL is already set, since an explicit root level is never overwritten.

VariableDefaultDescription
BAND_LOG_LEVELINFOLevel for the band logger
BAND_LOG_ROOT_LEVELWARNINGLevel for non-Band loggers
BAND_LOG_FILEunsetOptional file sink path
BAND_LOG_FILE_LEVELfollows BAND_LOG_LEVELLevel for the file sink
BAND_LOG_MAX_BYTES0Maximum file size before rotation; 0 disables rotation
BAND_LOG_BACKUPS1Rotated files to keep; at least 1 is required when rotation is enabled
BAND_LOG_CONSOLE_STYLEstandardstandard, rich, or json
BAND_LOG_FILE_STYLEstandardstandard or json
BAND_LOG_STREAMstderrstderr or stdout
BAND_LOG_OVERRIDES{}JSON object mapping logger names to levels, for example {"httpx":"WARNING"}

Explicit LogSettings constructor values take precedence over environment variables, which take precedence over the defaults above. An empty environment value falls back to the default. LogSettings.create(...) drops None fields, so an optional CLI flag that was not passed leaves the environment value alone:

1import argparse
2
3from band import LogSettings
4
5parser = argparse.ArgumentParser()
6parser.add_argument("--log-level")
7args = parser.parse_args([])
8
9LogSettings.create(log_level=args.log_level).configure()

BAND_LOG_OVERRIDES is merged with the extra_loggers mapping the application passes to configure(); the environment override wins when both name the same logger. configure_logging_from_env() is shorthand for LogSettings().configure().

Applying order and inspection

Both entry points call logging.config.dictConfig, which replaces the root handlers. Configure Band logging first, then attach any handlers your host application adds on top. To inspect or merge the configuration instead of applying it, build_logging_config() takes the same arguments as configure_logging() and LogSettings().build_config() takes the same arguments as configure(); both return the dictConfig dictionary.

OpenTelemetry

The host application owns OpenTelemetry providers, processors, exporters, and handlers. Band creates and exports no telemetry of its own. What it does provide is correlation: the json style includes otelTraceID, otelSpanID, otelTraceSampled, and otelServiceName in every record, so a log pipeline reads one shape whether or not the host instrumented the process. Without instrumentation those four fields serialize as null.

Configure Band logging before attaching an OpenTelemetry log handler, because dictConfig would otherwise drop it:

1from band import LogSettings
2
3LogSettings().for_application().configure()
4# Attach the host application's OpenTelemetry LoggingHandler here.

Select the json style to get the correlation fields, either with BAND_LOG_CONSOLE_STYLE=json in the environment or LogSettings(log_console_style=LoggingStyle.JSON) in code. Both need band-sdk[logging] installed; without it the SDK raises BandConfigError naming the missing python-json-logger dependency.

If you replace the default JSON field list with json_fields=..., splice OTEL_CORRELATION_FIELDS from band.logging_config back in or the correlation keys are dropped.

The Pydantic AI adapter accepts instrument=True, False, or an InstrumentationSettings instance and forwards it to Pydantic AI. Other frameworks are instrumented through their own OpenTelemetry integration, and remote or out-of-process model backends emit their model spans outside the Band SDK process. The SDK repository has a runnable host-owned OpenTelemetry example with console exporters, trace-context injection, and provider flush and shutdown.


Complete .env Example

.env
$# Platform URLs
$BAND_REST_URL=https://app.band.ai # Enables managing chats, participants, and agent settings
$BAND_WS_URL=wss://app.band.ai/api/v1/socket/websocket # Enables receiving and sending real-time messages
$
$# LLM API Keys
$OPENAI_API_KEY=sk-your-openai-key-here # Enables using GPT models for agent conversations
$ANTHROPIC_API_KEY=sk-ant-your-anthropic-key-here # Enables using Claude models for agent conversations
$
$# Optional: SDK logging
$# BAND_LOG_LEVEL=DEBUG # Raises the band logger; read by LogSettings, not by configure_logging()
$# BAND_LOG_CONSOLE_STYLE=json # Structured console output, requires band-sdk[logging]
$
$# Optional: LangSmith tracing
$# LANGCHAIN_API_KEY=ls-your-langsmith-key # Enables debugging agent conversations in LangSmith
$# LANGCHAIN_TRACING_V2=true # Enables viewing agent decision-making process
$# LANGCHAIN_PROJECT=my-agent-project # Organizes traces by project in LangSmith

Python-Level Configuration

The SDK also provides Python configuration objects for runtime behavior. See the SDK Reference for AgentConfig and SessionConfig documentation.


Security

1

Add Both Files to .gitignore

Both .env and agent_config.yaml contain secrets. Add them to .gitignore to prevent accidental commits.

$# .gitignore
$.env
$agent_config.yaml
2

Commit Your Own Example Files

The installed package ships no example files. Create .env.example and agent_config.yaml.example yourself, holding the same keys with placeholder values, and commit those so team members know what to fill in.

3

Use Separate Keys per Environment

Use different agent credentials for development, staging, and production. This limits the impact of a compromised key to a single environment.


Confirming a Successful Connection

Agent is running! Press Ctrl+C to stop. is a log line in the tutorial code, not in the SDK. It runs before agent.run() authenticates, so it appears verbatim even when the agent ID and API key are wrong. Read it as “the process started”, not as “the configuration works”.

The line that confirms a successful connection comes from the SDK’s own band.agent logger:

2026-01-15 09:30:00 [INFO] band.agent: Agent started: My Agent (band-sdk 3.0.0)

The SDK emits it only after it has authenticated against the REST API, fetched the agent’s metadata, and connected the WebSocket. Any failure in that sequence, including a 401 from invalid credentials, raises instead. The agent name in the line comes from the platform, so seeing your agent’s real name also confirms the credentials resolved to the agent you expect.

The line confirms that authentication succeeded, not which environment it succeeded against. REST and WebSocket resolve separately, so setting only one of BAND_REST_URL / BAND_WS_URL lets production credentials authenticate and print this line while messages stream from somewhere else. No log line reports either URL, so confirm the environment by checking that .env sets both variables, or by logging the values that reach Agent.create().

Earlier in the same startup sequence, band.platform.link confirms the WebSocket and band.runtime.platform_runtime confirms the runtime:

2026-01-15 09:30:00 [INFO] band.platform.link: Connected to platform
2026-01-15 09:30:00 [INFO] band.runtime.platform_runtime: Platform runtime started for agent: My Agent

Both lines are INFO records on the band logger tree, and Band logging is opt-in, so call configure_logging() or LogSettings().configure() before agent.run() or you will see neither.


Troubleshooting

IssueCauseSolution
Agent authenticates but sees no messagesOnly one of BAND_REST_URL / BAND_WS_URL is set, so the unset one resolved to Band CloudSet both variables or neither; each resolves independently and nothing reports the mismatch
Agent silently connects to Band Cloud despite a self-hosted .envload_dotenv() ran after Agent.create(), or not at allCall load_dotenv() before building the agent; the SDK reads the process environment, not the file
ConnectionRefusedErrorWrong platform URLVerify BAND_REST_URL and BAND_WS_URL match your environment
401 UnauthorizedInvalid agent credentialsCheck agent_id and api_key in agent_config.yaml
ValueError: Agent 'my_agent' not foundKey name missing from the config fileVerify the key name matches between your code and agent_config.yaml
FileNotFoundError: Config file not found at ...No agent_config.yaml in the working directoryCreate it as shown in Agent Credentials. The error text suggests copying agent_config.yaml.example, which the installed package does not contain
Every os.getenv() returns Noneload_dotenv() not called, or called after the value was readCall load_dotenv() as the first statement in main(), before any os.getenv(). ws_url=None and rest_url=None are not errors, they resolve to Band Cloud, so this fails quietly
LLM returns errorsMissing or invalid provider keyCheck the relevant *_API_KEY variable in .env