Backplanes Python SDK
The Backplanes Python SDK lets Python agents register with a Backplanes collector, capture LLM and HTTP telemetry automatically, propagate workflow context between agents, and persist agent identity locally — with one call to set up.
Table Of Contents
- Install
- Quick Start
- How Instrumentation Works
- Modes
- Configuration And Identity
- Running Alongside Your Application
- Custom Events
- What The SDK Covers
- Examples
- Docs
- Development
Install
pip install backplanes
For local development:
uv pip install -e .
Quick Start
Get a key file from the dashboard: Settings → API keys → Create key → Download key file, and save it as ~/.backplanes/backplanes-key.json. (Or set BACKPLANES_ORG_API_KEY — the key file additionally carries a signing key, which you need to hand workflows to other agents.)
export BACKPLANES_ORG_API_KEY="OK_..." # alternative to the key file
Then instrument your agent with one call:
import anthropic
import backplanes
backplanes.init(agent_name="my-agent")
sdk = anthropic.Anthropic()
response = sdk.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=50,
messages=[{"role": "user", "content": "Say hello in five words."}],
)
That's the whole integration. Every Anthropic or OpenAI client constructed after init() is instrumented automatically — the call above lands in the dashboard as an llm event with provider, resolved model, and prompt/completion token counts, including for streaming calls. This script contains zero hand-written telemetry.
init() returns a BackplanesClient for everything beyond automatic capture — starting workflows, custom events, context propagation:
client = backplanes.init(agent_name="my-agent")
workflow_id = client.start_workflow()
How Instrumentation Works
Instrumentation is three composable layers, highest fidelity first. See docs/design/INSTRUMENTATION_TIERS.md for the full design.
SDK wrappers (wrap_anthropic, wrap_openai) wrap an SDK client instance's own create/stream methods, recording each call at the semantic level: resolved model, prompt and completion tokens, and cache tokens. Streaming responses are observed chunk by chunk, so token counts land even for streamed calls. Use these directly when you want explicit per-client control:
import anthropic
import backplanes
client = backplanes.init(agent_name="my-agent", auto_instrument=False)
sdk = backplanes.wrap_anthropic(anthropic.Anthropic(), client)
init() constructor hooks give you wrapper fidelity without touching client construction: init() hooks the Anthropic/OpenAI SDK constructors, so every client built afterwards comes out wrapped. Clients constructed before init() fall through to the transport net.
The transport net (patch_httpx, patch_requests) instruments outgoing HTTP at the httpx (sync and async) and requests level. It catches everything the wrappers don't — unknown SDKs, raw HTTP, pre-init() clients — recognizing known inference endpoints and emitting llm events with provider, model, and token counts where the response allows. It also carries Backplanes-Context propagation to every destination. Streamed responses caught only at this level record the call but not token usage, and are marked as such in event metadata.
The layers compose because of one dedup guarantee: one logical call produces one event. Wrappers mark the duration of the underlying SDK call, and the transport net demotes itself inside that window — context propagation still runs, event emission does not. An SDK's internal retries land inside the same window, so they never double-count.
Modes
Workflow Root
Use workflow root mode when your agent starts workflows. This needs an org API
key, passed directly or read from BACKPLANES_ORG_API_KEY.
client = backplanes.init(
org_api_key="OK_...",
agent_name="my-agent",
)
A key file bundles the same org API key with an Ed25519 signing key, and the client reads both from it:
client = backplanes.init(
config_file="backplanes-key.json",
agent_name="my-agent",
)
Without an explicit config_file, the client searches BACKPLANES_KEY_FILE, ./backplanes-key.json, and ~/.backplanes/backplanes-key.json — see docs/CONFIGURATION.md.
Propagating To Other Agents
Handing a workflow to a second agent means signing a Backplanes-Context, and
that needs the signing key from a key file. An org API key on its own won't do
it. can_propagate_context tells you which you have. Without a signing key each
agent's events still carry their own workflow ID, they just aren't linked into
one chain.
The Backplanes-Context header identifies your org, workflow, and agent chain,
and the transport patches send it to every destination:
backplanes.init(agent_name="my-agent")
That is what lets a chain link without anyone describing the topology first, and it has a disclosure consequence worth being explicit about: the same process usually calls both your own services and third-party APIs, and those third parties receive the header too.
Mid-Chain Agent
Use mid-chain mode when your agent receives a Backplanes-Context header from an upstream agent:
client = backplanes.init(agent_name="worker-agent")
context_jwt = request.headers.get("Backplanes-Context")
with client.request_context(context_jwt):
... # work in this scope is attributed to the upstream workflow
Local Development
By default the client targets api.backplanes.com:443 with TLS enabled. To point the same code at a different collector, use the environment:
export BACKPLANES_COLLECTOR_HOST=localhost
export BACKPLANES_COLLECTOR_PORT=50051
For a collector behind a private certificate authority, keep TLS on and point
BACKPLANES_TLS_CA_FILE at the CA bundle — gRPC does not read the OS trust
store. The same settings are available as constructor parameters
(collector_host, collector_port, tls_ca_cert); precedence is
explicit parameter > key file > environment > default.
Claiming And Unclaimed Mode
A first run without org credentials registers the agent and logs a claim URL.
Claim it in the dashboard to link the agent to your organization; the client
picks the claim up on its next call and starts sending. With a key file or
BACKPLANES_ORG_API_KEY in place, the agent is active immediately and no
claiming step is needed.
Until an agent is claimed:
start_workflow()returns a placeholder workflow IDget_context_header()returns{}because there is no active signed workflow contextingest()returns a stub response instead of sending events
An agent leaves unclaimed mode two ways:
- A key file appears in one of the standard search paths. The client hot-reloads it on the next call and gains org credentials and signing keys, so it can also mint
Backplanes-ContextJWTs for downstream agents. - The collector reports the agent as claimed.
refresh_claim_status()asks it directly, and the client polls at most once a minute during normal calls. This is what happens when you claim an agent from the dashboard, where there's no key file to download. The client can ingest and stamps its org onto events. Signing context JWTs still needs a key file.
Configuration And Identity
Every collector setting resolves from constructor parameters, the key file, and
the environment, in that order. Key files only override the collector settings
they actually specify. Named agents get their own identity file
(~/.backplanes/identities/<agent_name>.json), so several agents on one
machine stay distinct identities. The full environment variable table, key file
schema, and identity file locations are in
docs/CONFIGURATION.md.
Running Alongside Your Application
If the collector has problems, your application should keep working.
-
Construction does not raise by default. When the collector is unreachable the client logs a warning and starts up with telemetry off, retrying registration in the background. Pass
strict=Trueif you would rather it raiseBackplanesNotReadyError— useful in CI, where silent telemetry is worse than a failed build. -
is_readyanswers "would an event reach the collector". It is False while the agent is waiting to be claimed, which is where every new agent starts.has_identityis the narrower question of whether registration succeeded. An unclaimed client warns, so this state is visible without checking. -
Every call has a timeout, 10 seconds by default and configurable. A host that drops packets silently will fail the call instead of hanging your thread.
-
Ingest never blocks you.
ingest()starts the call and hands back the future for it. gRPC does the network work on its own threads. Ask for the answer only when you want it:client.ingest(event) # fire and forget client.ingest(event).result() # wait for the collector's counts
At interpreter exit the client waits up to three seconds for whatever is still in flight, so a short script usually doesn't need to do anything. That wait is best effort and gives up quietly — if you need to know an event was accepted, call
result().flush(timeout=...)waits for everything outstanding and returns False if it ran out of time, which is what you want before returning from a serverless handler. A call nobody waits on logs its failure, since there's nobody to raise to.
Custom Events
Automatic instrumentation covers LLM and HTTP traffic. For everything else —
tool invocations, internal pipeline stages, domain-specific events —
create_event(...) and ingest(...) are still there:
client = backplanes.init(agent_name="my-agent")
event = client.create_event(
edge_type="tool",
direction="egress",
status="ok",
metadata={"tool.name": "send_email"},
)
client.ingest(event)
If your agent is handling an incoming Backplanes workflow, wrap the work in
request_context(...) so events are attributed to the upstream chain:
context_jwt = request.headers["Backplanes-Context"]
with client.request_context(context_jwt):
client.ingest(client.create_event(edge_type="tool", direction="ingress"))
create_event(...) supports the full event schema — sessions, instance IDs,
parent events, PII fields, token usage, error info, custom metadata. See
docs/ADVANCED.md, and
examples/email_campaign for tool and PII events in
a real workflow.
What The SDK Covers
init(...)— one-call setup: constructs the client, hooks the Anthropic/OpenAI SDK constructors, installs the transport netwrap_anthropic(...)/wrap_openai(...)— semantic wrappers for explicit per-client instrumentation, with streaming token capture and cache token metadata; the OpenAI wrapper coverschat.completions,responses, andembeddings, and attributes OpenAI-compatible endpoints (gateways, Groq, and the like) bybase_urlpatch_httpx(...)— the transport net for anything built onhttpx, sync and async clients. Recognized inference endpoints emit anllmedge with provider, resolved model, and token counts pulled from the responsepatch_requests(...)— the same net for outgoingrequestscallsinstrument_fastapi(...),instrument_asgi(...), andinstrument_wsgi(...)— one-line framework setup for inbound context and ingress telemetry (Django is WSGI/ASGI)request_context(...)— request-scoped propagation in web apps and workerscreate_event(...)— the full event schema, includingsession_id, instance IDs,parent_event_id, PII fields,schema_version, token usage, error info, and custom metadatacreate_batch(...),ingest_batch(...), andingest_stream(...)— explicit batch construction, unary batch ingest, and the collector's client-streaming RPC
Both HTTP patches record calls to hosts you don't own against an ID derived from the hostname, so you can see which third party received the data. That is unconditional — it needs no argument, and sends nothing to the third party.
The Backplanes-Context header is also unconditional: every destination gets
the org, workflow, and agent chain while a workflow context is active. That
includes third-party APIs, so enable the transport patches only where that
disclosure is acceptable:
patch_httpx(client)
patch_requests(client)
You can also import the generated proto modules directly:
from backplanes.v1 import collector_pb2, events_pb2
Examples
Runnable, real-world examples — from a five-line quickstart to a multi-agent
workflow with context propagation — live in examples/, each with
its own README.
Docs
Focused docs live in docs/:
- Configuration And Identity
- Advanced Usage
- Integrations
- Framework And App Placement
- Context Model
- FastAPI And ASGI Apps
- Django And WSGI/ASGI Apps
- A2A / A2P / MCP / Worker Patterns
- Releasing
Development
uv sync
uv build
uv run ruff check .
uv run pytest
Regenerating Protobuf Bindings
backplanes/v1/ is generated from the Backplanes proto/ buf module and
checked in, so installing this package needs neither buf nor protoc. To pick
up proto changes, point BACKPLANES_PROTO_REPO at a checkout holding that
module:
BACKPLANES_PROTO_REPO=/path/to/checkout ./scripts/gen-proto.sh
Plugin versions are pinned in buf.gen.yaml so the output is
reproducible. The protocolbuffers/python plugin decides the gencode version
stamped into every _pb2.py, and the protobuf runtime won't load gencode newer
than itself. So if you bump that pin, bump the protobuf floor in
pyproject.toml too. The script fails when the two disagree, which is better
than an import error in someone else's app.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file backplanes-0.1.0.tar.gz.
File metadata
- Download URL: backplanes-0.1.0.tar.gz
- Upload date:
- Size: 142.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.12.2 {"installer":{"name":"uv","version":"0.12.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d907303e4b57f7ccd0885a023dfabbc6d798a58602b5052167f5405000f6af11
|
|
| MD5 |
4c2a4c7b9ac3137b1fb3bf0c35a95442
|
|
| BLAKE2b-256 |
57e8251b3c948f0534ff3bbeda4b5653b3f4825647f77469b2715f1545d2cac5
|
File details
Details for the file backplanes-0.1.0-py3-none-any.whl.
File metadata
- Download URL: backplanes-0.1.0-py3-none-any.whl
- Upload date:
- Size: 118.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.12.2 {"installer":{"name":"uv","version":"0.12.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4bae54b70f1750fc71ab484a0cfc636c6e0e2c3c28e07d2dac72cb7a2b8f938
|
|
| MD5 |
a8176eeca05bfcb988509e2b3e6ae922
|
|
| BLAKE2b-256 |
4a289be3403517ed9c25dcd98ae1bd1100d692060584d4a5350f201c73b83233
|