Amplifier Foundation

Application
Integration Guide

From "I understand bundles" to
"I've built a production app with Amplifier"

Web Apps Voice Assistants Slack Bots CLI Tools Background Services
February 2026 · Active
The Problem

The integration gap is where
most projects stall

📘

You know bundles

You understand YAML frontmatter, tools, hooks, and the prepare/session lifecycle. The concepts make sense.

🤔

But how do you connect?

How does FastAPI talk to Amplifier? Where do WebSocket events go? What about Slack threads? Voice streams?

🏗️

Without a pattern, you guess

Direct API calls. Hand-rolled tool loops. Duplicated conversation state. All the benefits of Amplifier — lost.

This guide gives you the one architectural principle and five session patterns that make every integration clean.

Core Concept

The Protocol Boundary Principle

Your app and Amplifier meet at a clean boundary — a thin bridge where app events become Amplifier operations and Amplifier events become app outputs. Neither side knows the other's internals.

Your Application
FastAPI routes
WebSocket handlers
Slack event listeners
Audio stream management
UI rendering logic
State management
ApprovalSystem
DisplaySystem
StreamingHook
Spawn Capability
Amplifier
Session lifecycle
Orchestrator loop
Tool dispatch
Provider management
Hook system
Context management
The Boundary

Four protocol points where
your app meets Amplifier

ApprovalSystem

Amplifier → App
Called when a tool or hook requests human confirmation. Your app decides how to present the approval — modal dialog, Slack button, CLI prompt — and returns the decision.

🖥️

DisplaySystem

Amplifier → App
Called when an agent wants to show something to the user. Your app decides the rendering medium — WebSocket message, Slack block, terminal output.

StreamingHook

Amplifier → App
A hook that receives all session events — content deltas, tool status, thinking, errors — and forwards them over your transport: WebSocket, SSE, Slack.

🔀

Spawn Capability

Amplifier → App
Called when any component needs a new session — agent delegation, sub-tasks, recipe steps, parallel analysis. The universal “create a new session” entry point.

Benefits

Respect the boundary,
get everything for free

Testability

Mock either side independently. Test Amplifier without your web framework. Test your web framework without Amplifier.

Portability

Same Amplifier session behind web, voice, CLI, or Slack. The boundary adapts; the intelligence stays the same.

Clarity

Know which side to debug when something breaks. App-side or Amplifier-side — never both tangled together.

Evolvability

Swap your web framework without touching Amplifier. Swap your orchestrator without touching your web framework.

# Test Amplifier logic without the web framework session = await prepared.create_session( approval_system=MockApproval(), display_system=MockDisplay(), ) response = await session.execute("What's on my calendar?") assert "meeting" in response.lower()
Lifecycle

The universal session lifecycle

Every Amplifier application follows these 7 steps, regardless of app type.

1. LOAD
2. COMPOSE
3. PREPARE
4. CREATE
5. MOUNT
6. HOOK
7. EXECUTE
# Steps 1-3: Once at startup bundle = await load_bundle("./bundle.md") prepared = await bundle.prepare() # Steps 4-7: Per interaction session = await prepared.create_session( session_id="my-session-001", approval_system=my_approval, display_system=my_display, ) response = await session.execute("Hello!")
StepRequiredWhen
LoadYesFile, name, or git URI
ComposeNoRuntime overlays needed
PrepareYesOnce at startup
CreateYesPer-request or per-user
MountNoRuntime-dependent tools
HookNoApp-specific events
ExecuteYesRuns the agent loop
Key Insight

PreparedBundle
is your singleton

Sessions are ephemeral.

prepare()

Expensive — downloads modules, resolves dependencies, activates everything

Do once at startup
create_session()

Cheap — produces an AmplifierSession from the cached PreparedBundle

Do per-request
Composition

Three bundle composition strategies

📄

Declarative

Everything in bundle.md YAML. Good for stable configs that rarely change at runtime.

# bundle.md frontmatter includes: - bundle: git+https://... session: orchestrator: loop-streaming context: context-simple
🐍

Programmatic

Build bundles in Python at startup. Good for dynamic config based on environment or user.

base = Bundle(name="my-app", ...) if ENV == "development": overlay = Bundle( providers=[{"debug": True}] ) bundle = base.compose(overlay)
🔗

Hybrid Most Common

Load stable base from file, compose runtime overlays in code. Versioned YAML + dynamic injection.

bundle = await load_bundle("./bundle.md") overlay = Bundle(providers=[{ "model": os.environ["MODEL"] }]) composed = bundle.compose(overlay) prepared = await composed.prepare()
Tools

Two tool mounting patterns

📦

In-Bundle Declaration

Declared in YAML frontmatter. Resolved during prepare(), mounted automatically. For tools fundamental to the bundle's identity.

tools: - module: tool-filesystem source: git+https://... - module: my-graph-tool source: local:modules/graph
  • Core bundle capability
  • Available to ALL sessions
  • Discoverable in bundle YAML
🔌

Post-Creation Mounting

Mounted via coordinator.mount() after the session exists. For tools that depend on runtime state.

session = await prepared.create_session(...) # Needs live DB connection graph = GraphTool(driver=app.neo4j) await session.coordinator.mount( "tools", graph, name="life_graph" )
  • Depends on runtime state (DB, API)
  • App-specific (Slack, UI rendering)
  • Varies per session or per user
Hooks

Bundle hooks vs ephemeral hooks

🏛️

Bundle Hooks Persistent

Declared in YAML. Live for the session lifetime. Cross-cutting concerns that should always be active.

Event logging CXDB capture Privacy redaction Cost tracking
⏱️

Ephemeral Hooks Per-Execution

Registered before execution, unregistered in finally. App-specific event handling per interaction.

WebSocket streaming Slack thread updates Progress display

⚡ The Streaming Hook Pattern

Implement a hook that receives ALL session events and forwards them over your transport — this makes the agent's work visible in real-time.

class WebSocketStreamingHook: def __init__(self, websocket): self.ws = websocket async def on_event(self, event_type, data): await self.ws.send_json({ "type": "session_event", "event": event_type, "data": data, }) # Register per-connection, clean up in finally unreg = session.coordinator.hooks.register("*", hook.on_event)
Patterns

Five session lifecycle patterns

A: Per-Request

Each request is independent. Create → execute → discard. REST APIs, webhooks, document processing.

Seconds

B: Per-Conversation

Multiple messages form a conversation. Session map keyed by channel ID. Chat bots, messaging.

Minutes–Hours

C: Singleton

One user, one agent, continuous context. The session IS the app. Personal AI assistants.

Days–Weeks

D: Voice Bridge

Voice model handles audio I/O. Amplifier handles tools and reasoning. They meet at the tool boundary.

Session Duration

E: Multi-Session

Multiple bundles, multiple sessions, routing logic. Multi-tenant, A/B testing, ensemble agents.

Varies
# Pattern B: Per-Conversation — the most common integration pattern sessions: dict[str, AmplifierSession] = {} async def handle_message(conversation_id: str, message: str): if conversation_id not in sessions: sessions[conversation_id] = await prepared.create_session( session_id=conversation_id, approval_system=SlackApproval(conversation_id), ) return await sessions[conversation_id].execute(message)
Choosing a Pattern

Pattern decision matrix

Application Type Pattern Lifespan Key Concern
REST API / webhook A: Per-Request Seconds PreparedBundle singleton
Chat bot / messaging B: Per-Conversation Min–Hours Session map + per-session locks
Personal AI assistant C: Singleton Days–Weeks Persistence + compound intelligence
Voice assistant + tools D: Voice Bridge Session Tool boundary, not orchestrator replacement
Multi-persona / tenant E: Multi-Session Varies Routing + resource management
Voice + web + proactive C + D Process lifetime Singleton provides tools to voice bridge

🔑 Pattern D insight

The voice model is NOT inside AmplifierSession. It has its own connection, its own session state. Amplifier is a tool backend, not the orchestrator of the voice conversation. Audio never touches Amplifier — only text (tool calls, results) crosses the boundary.

Persistence

Three levels of session persistence

Level 1: Session ID Reuse Built-In

Pass the same session_id across restarts. The context module recognizes the ID and reconnects. Lightest touch — works out of the box.

# First run session = await prepared.create_session(session_id="user-42") # After restart — same ID reconnects to context session = await prepared.create_session(session_id="user-42")

Level 2: Manual Context Save/Restore

Reach into the context module to save and restore conversation history. Full control over serialization and storage.

ctx = session.coordinator.mount_points.get("context") messages = await ctx.get_messages() Path(path).write_text(json.dumps(messages))

Level 3: Persistent Context Module

The context module handles persistence transparently. Declare in YAML, storage is automatic.

session: context: module: context-persistent config: storage_path: ./data/sessions auto_save: true

Session ID reuse also enables hot reconfiguration — tear down and recreate with the same ID but different providers, tools, hooks, or orchestrator. Context persists; configuration evolves.

Avoid These

Six anti-patterns that
defeat the purpose

1. Direct API Calls

Calling anthropic.messages.create() directly, parsing tool calls manually. You've reimplemented the orchestrator — without hooks, streaming, or error recovery.

2. Decorative bundle.md

A valid bundle.md exists but the app never calls load_bundle(). Code constructs everything independently. The bundle drifts from reality.

3. Hand-Rolled Tool Loops

A while True loop that calls the provider, checks for tool calls, executes them. This is the orchestrator's job — it handles streaming, hooks, error recovery.

4. Ignoring the Boundary

A FastAPI handler directly accesses coordinator.mount_points, manages conversation state. App and Amplifier layers become entangled.

5. Fat Bundle Per-Request

Calling prepare() per request. It downloads modules every time. Fix: prepare once at startup, create sessions cheaply.

6. Singleton for Multi-User

A multi-user chat bot with one shared session. User A's context bleeds into User B's conversation. Fix: Pattern B — one session per conversation.

Sources

Sources & Methodology

Data as of: February 26, 2026

Feature status: Active

Source document: amplifier-foundation/docs/APPLICATION_INTEGRATION_GUIDE.md

Content derived from:

Referenced modules (all reference implementations):

Note: All modules referenced in this deck are reference implementations. The Amplifier ecosystem encourages developers to study them for understanding, then build custom implementations tailored to their specific needs.

Get Started

Build your first
Amplifier integration

One boundary. Four protocols. Five patterns.
Pick the one that fits your app and go.

📖

Read the Full Guide

APPLICATION_INTEGRATION_GUIDE.md in amplifier-foundation/docs

🧪

Study Examples

Examples 07, 08, 14, 18 show real integration patterns

🏗️

Start Building

load_bundle → prepare → create_session → execute

More Amplifier Stories