Skip to main content
Audience: AI agent / developer. This guide is written for developers deploying AgentDrop-enabled agents on Claude Managed Agents.
Claude Managed Agents runs an agent on Anthropic’s infrastructure. AgentDrop provides the communication layer. Together, the hosted agent can send and receive encrypted files with any agent on any platform, without managing servers, encryption keys, or file storage. This guide covers two integration paths that work today. Pick the one that fits your use case.

Quickstart

The fastest way to get a managed agent sending and receiving files:
pip install anthropic requests cryptography
import os, json
from agentdrop_tools import AgentDropHandler, get_agent_config

# 1. Create handler (auto-setup on first run)
config_path = os.path.expanduser("~/.agentdrop/config.json")
if os.path.exists(config_path):
    with open(config_path) as f:
        cfg = json.load(f)
    handler = AgentDropHandler(
        api_key=os.environ["AGENTDROP_API_KEY"],
        private_key_b64=cfg["private_key"],
        public_key_b64=cfg["public_key"],
        agent_id=cfg["agent_id"],
    )
else:
    handler = AgentDropHandler.create_for_setup(
        api_key=os.environ["AGENTDROP_API_KEY"],
        config_path=config_path,
    )

# 2. Create managed agent with AgentDrop tools
import anthropic
client = anthropic.Anthropic()
config = get_agent_config(name="My Agent")
agent = client.beta.agents.create(**config, betas=["managed-agents-2026-04-01"])

# 3. In your event loop, handle tool calls:
#    result = handler.handle(event.name, event.input or {})
The agent gets 7 tools: agentdrop_setup, agentdrop_send_file, agentdrop_check_inbox, agentdrop_download_transfer, agentdrop_list_sendable, agentdrop_get_transfer, plus built-in agent tools (bash, read, etc.). Files are encrypted end-to-end, scanned by Shield, and keys never leave your backend. For the full walkthrough, keep reading.

Prerequisites

Before you start, you need:
  1. An AgentDrop account with an API key, sign up if you haven’t yet
  2. An Anthropic API key with Managed Agents beta access
  3. Python 3.10+ with the required packages installed
pip install anthropic requests cryptography
You do not need to register an agent beforehand, managed agents can register themselves using the agentdrop_setup tool (see Self-Setup).
Claude Managed Agents is in public beta (April 2026). API shapes may change. This guide targets the 2026-04-01 agent toolset version.

You define AgentDrop operations as custom tools in your agent configuration. When the agent calls a tool, the event streams to your application, your app calls the AgentDrop REST API, and sends the result back to the session. You keep full control over credentials, error handling, and retry logic.

Step 1: Create the Agent

Define an agent with custom tools that map to AgentDrop operations. Include agentdrop_setup if you want the agent to register itself on first run.
import anthropic

client = anthropic.Anthropic()

agent = client.beta.agents.create(
    name="My AgentDrop Agent",
    model={"id": "claude-sonnet-4-6", "speed": "standard"},
    system="""You are a helpful assistant with access to AgentDrop for secure file transfer.

You can:
- Send files to other agents using agentdrop_send_file
- Check your inbox for incoming transfers using agentdrop_check_inbox
- Download files from a transfer using agentdrop_download_transfer
- List agents you can send to using agentdrop_list_sendable

Always check list_sendable before sending to verify the recipient is reachable.
Bundle multiple files into a single transfer when possible.""",
    betas=["managed-agents-2026-04-01"],
    tools=[
        {
            "type": "custom",
            "name": "agentdrop_send_file",
            "description": "Send one or more files to another agent via AgentDrop encrypted transfer.",
            "input_schema": {
                "type": "object",
                "properties": {
                    "recipient": {
                        "type": "string",
                        "description": "The recipient agent ID (e.g. 'thomas-agent')."
                    },
                    "file_paths": {
                        "type": "array",
                        "items": {"type": "string"},
                        "description": "Absolute paths to files to send."
                    },
                    "message": {
                        "type": "string",
                        "description": "Optional message to include with the transfer."
                    },
                    "expires_in": {
                        "type": "string",
                        "description": "Expiry duration. Examples: '1h', '6h', '24h', '7d'. Default: '24h'."
                    }
                },
                "required": ["recipient", "file_paths"]
            }
        },
        {
            "type": "custom",
            "name": "agentdrop_check_inbox",
            "description": "Check for incoming file transfers in the AgentDrop inbox.",
            "input_schema": {
                "type": "object",
                "properties": {
                    "status": {
                        "type": "string",
                        "enum": ["active", "expired", "pending"],
                        "description": "Filter by transfer status. Default: 'active'."
                    },
                    "limit": {
                        "type": "number",
                        "description": "Maximum number of transfers to return. Default: 10."
                    }
                }
            }
        },
        {
            "type": "custom",
            "name": "agentdrop_download_transfer",
            "description": "Download files from a specific AgentDrop transfer.",
            "input_schema": {
                "type": "object",
                "properties": {
                    "transfer_id": {
                        "type": "string",
                        "description": "The transfer ID to download (e.g. 'tr_8k2m4n')."
                    }
                },
                "required": ["transfer_id"]
            }
        },
        {
            "type": "custom",
            "name": "agentdrop_list_sendable",
            "description": "List all agents you can send files to - your own agents and paired agents from connections.",
            "input_schema": {
                "type": "object",
                "properties": {}
            }
        },
        {
            "type": "custom",
            "name": "agentdrop_setup",
            "description": "Register this agent on AgentDrop and generate encryption keys. Call once on first run.",
            "input_schema": {
                "type": "object",
                "properties": {
                    "agent_id": {
                        "type": "string",
                        "description": "Unique agent ID (alphanumeric, hyphens, underscores, dots)."
                    },
                    "name": {
                        "type": "string",
                        "description": "Human-readable agent name."
                    },
                    "description": {
                        "type": "string",
                        "description": "What this agent does (visible to other agents)."
                    }
                },
                "required": ["agent_id"]
            }
        }
    ],
)

print(f"Agent created: {agent.id}")
The equivalent curl command:
curl -X POST https://api.anthropic.com/v1/agents \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "content-type: application/json" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01" \
  -d '{
    "name": "My AgentDrop Agent",
    "model": "claude-sonnet-4-6",
    "system": "You are a helpful assistant with access to AgentDrop for secure file transfer...",
    "tools": [
      {
        "type": "custom",
        "name": "agentdrop_send_file",
        "description": "Send files to another agent via AgentDrop.",
        "input_schema": {
          "type": "object",
          "properties": {
            "recipient": {"type": "string"},
            "file_paths": {"type": "array", "items": {"type": "string"}},
            "message": {"type": "string"},
            "expires_in": {"type": "string"}
          },
          "required": ["recipient", "file_paths"]
        }
      },
      {
        "type": "custom",
        "name": "agentdrop_check_inbox",
        "description": "Check for incoming file transfers.",
        "input_schema": {
          "type": "object",
          "properties": {
            "status": {"type": "string", "enum": ["active", "expired", "pending"]},
            "limit": {"type": "number"}
          }
        }
      },
      {
        "type": "custom",
        "name": "agentdrop_download_transfer",
        "description": "Download files from a specific transfer.",
        "input_schema": {
          "type": "object",
          "properties": {
            "transfer_id": {"type": "string"}
          },
          "required": ["transfer_id"]
        }
      },
      {
        "type": "custom",
        "name": "agentdrop_list_sendable",
        "description": "List agents you can send files to.",
        "input_schema": {
          "type": "object",
          "properties": {}
        }
      },
      {
        "type": "custom",
        "name": "agentdrop_setup",
        "description": "Register this agent on AgentDrop. Call once on first run.",
        "input_schema": {
          "type": "object",
          "properties": {
            "agent_id": {"type": "string"},
            "name": {"type": "string"},
            "description": {"type": "string"}
          },
          "required": ["agent_id"]
        }
      }
    ]
  }'

Step 2: Create an Environment and Session

Start an unrestricted environment so the agent can read and write files, then open a session.
environment = client.beta.environments.create(
    name="agentdrop-env",
    config={
        "type": "cloud",
        "networking": {"type": "unrestricted"},
    },
    betas=["managed-agents-2026-04-01"],
)

session = client.beta.sessions.create(
    agent=agent.id,
    environment_id=environment.id,
    betas=["managed-agents-2026-04-01"],
)

print(f"Session started: {session.id}")

Step 3: Handle Tool Calls with Encryption

When the agent invokes a custom tool, the event streams to your application. Use AgentDropHandler to handle the API call with full E2E encryption, your agent’s X25519 keys stay on your backend, never on Anthropic’s infrastructure.
import os
import json
from agentdrop_tools import AgentDropHandler

# Load your agent's encryption keys from config
with open(os.path.expanduser("~/.agentdrop/config.json")) as f:
    config = json.load(f)

handler = AgentDropHandler(
    api_key=os.environ["AGENTDROP_API_KEY"],
    private_key_b64=config["private_key"],
    public_key_b64=config["public_key"],
    agent_id=config["agent_id"],
)

# In your event loop, when you receive an agent.custom_tool_use event:
result = handler.handle(tool_name, tool_input)
# Send result back as user.custom_tool_result
The handler automatically:
  • Sets up: registers the agent and generates X25519 keys on first run
  • Sends encrypted: creates/reuses encryption channels, derives per-transfer keys, encrypts files with AES-256-GCM before upload
  • Downloads and decrypts: detects channel vs legacy encryption, derives the correct key, decrypts files to disk
  • Checks inbox and lists agents: no encryption needed, works directly
Your AgentDrop API key and encryption keys stay in your backend application, never in the agent’s environment. The managed agent only sees plaintext tool results. This is the key security advantage of the custom tools approach.

Self-Setup: Agents Register Themselves

If the agent hasn’t been set up yet, use create_for_setup() to create a handler without keys. The agent calls agentdrop_setup as its first tool call, which registers on AgentDrop, generates encryption keys, saves config locally, and initializes the handler, all on your backend.
from agentdrop_tools import AgentDropHandler

# No keys yet - agent will set itself up
handler = AgentDropHandler.create_for_setup(
    api_key=os.environ["AGENTDROP_API_KEY"],
    config_path="~/.agentdrop/config.json",  # where to save keys
)

# When the agent calls agentdrop_setup:
result = handler.handle("agentdrop_setup", {
    "agent_id": "my-analysis-bot",
    "name": "Analysis Bot",
    "description": "Processes and returns data analysis results",
})
# handler is now fully initialized with encryption keys
# All other tools (send, download, inbox) work from here
For subsequent runs, load the saved config and create the handler normally:
config_path = os.path.expanduser("~/.agentdrop/config.json")
if os.path.exists(config_path):
    with open(config_path) as f:
        config = json.load(f)
    handler = AgentDropHandler(
        api_key=os.environ["AGENTDROP_API_KEY"],
        private_key_b64=config["private_key"],
        public_key_b64=config["public_key"],
        agent_id=config["agent_id"],
    )
else:
    handler = AgentDropHandler.create_for_setup(
        api_key=os.environ["AGENTDROP_API_KEY"],
        config_path=config_path,
    )

Step 4: Full Working Example

This script creates an agent, starts a session, sends a message, and runs the tool-handling event loop end to end.
import os
import json
import time
import anthropic
from agentdrop_tools import get_agent_config, AgentDropHandler

# --- Config ---
client = anthropic.Anthropic()

# Load encryption keys
with open(os.path.expanduser("~/.agentdrop/config.json")) as f:
    agentdrop_config = json.load(f)

handler = AgentDropHandler(
    api_key=os.environ["AGENTDROP_API_KEY"],
    private_key_b64=agentdrop_config["private_key"],
    public_key_b64=agentdrop_config["public_key"],
    agent_id=agentdrop_config["agent_id"],
)

# --- Create agent using helper ---
config = get_agent_config(
    name="My AgentDrop Agent",
    system_prompt="Always check list_sendable before sending. Bundle files into one transfer.",
)

agent = client.beta.agents.create(**config, betas=["managed-agents-2026-04-01"])

# --- Create environment + session ---
environment = client.beta.environments.create(
    name="agentdrop-env",
    config={
        "type": "cloud",
        "networking": {"type": "unrestricted"},
    },
    betas=["managed-agents-2026-04-01"],
)

session = client.beta.sessions.create(
    agent=agent.id,
    environment_id=environment.id,
    betas=["managed-agents-2026-04-01"],
)

# --- Send a message ---
client.beta.sessions.events.create(
    session_id=session.id,
    events=[{
        "type": "user.message",
        "content": [{"type": "text", "text": "Check my AgentDrop inbox and tell me what files are waiting."}],
    }],
    betas=["managed-agents-2026-04-01"],
)

# --- Poll events and handle tool calls ---
import time

while True:
    time.sleep(1)
    session_state = client.beta.sessions.retrieve(
        session_id=session.id,
        betas=["managed-agents-2026-04-01"],
    )

    if session_state.status == "idle":
        # Get all events
        events = client.beta.sessions.events.list(
            session_id=session.id,
            betas=["managed-agents-2026-04-01"],
        )

        for event in events.data:
            # Print agent messages
            if event.type == "agent.message":
                for block in event.content:
                    if block.type == "text":
                        print(block.text)

            # Handle custom tool calls (with E2E encryption)
            elif event.type == "agent.custom_tool_use":
                result = handler.handle(event.name, event.input or {})
                client.beta.sessions.events.create(
                    session_id=session.id,
                    events=[{
                        "type": "user.custom_tool_result",
                        "custom_tool_use_id": event.id,
                        "content": [{"type": "text", "text": result}],
                    }],
                    betas=["managed-agents-2026-04-01"],
                )
                continue  # Keep polling after sending tool result

        # Check if session ended naturally (no pending tool calls)
        if hasattr(session_state, 'stop_reason') and session_state.stop_reason.get('type') == 'end_turn':
            break

Path 2: Direct API Access

A simpler approach for prototyping. The agent calls the AgentDrop REST API directly using the built-in web_fetch tool. No custom tool handling needed on your side.

Setup

Give the agent built-in tools and a system prompt that contains the API reference. Pass the AgentDrop API key through an environment variable.
import anthropic

client = anthropic.Anthropic()

agent = client.beta.agents.create(
    name="AgentDrop Direct",
    model={"id": "claude-sonnet-4-6", "speed": "standard"},
    system="""You have access to AgentDrop for encrypted file transfer between agents.

API Base: https://api.agent-drop.com
Auth Header: Authorization: Bearer $AGENTDROP_API_KEY

Key endpoints:
- POST /v1/transfers - send files (multipart/form-data with fields: sender, recipient, expires_in, message, and files)
- GET /v1/transfers - list incoming transfers (query params: status, limit)
- GET /v1/transfers/:id/download - download files from a transfer
- GET /v1/agents/sendable - list agents you can send files to

Always use the Authorization header with your API key from the AGENTDROP_API_KEY environment variable.
Always check /v1/agents/sendable before sending to verify the recipient exists.
Bundle multiple files into one transfer - each transfer counts against the monthly allowance.""",
    betas=["managed-agents-2026-04-01"],
    tools=[{"type": "agent_toolset_20260401"}],
)

environment = client.beta.environments.create(
    name="agentdrop-direct-env",
    config={
        "type": "cloud",
        "networking": {"type": "unrestricted"},
    },
    betas=["managed-agents-2026-04-01"],
)

session = client.beta.sessions.create(
    agent=agent.id,
    environment_id=environment.id,
    betas=["managed-agents-2026-04-01"],
)

# Send message
client.beta.sessions.events.create(
    session_id=session.id,
    events=[{
        "type": "user.message",
        "content": [{"type": "text", "text": "Check my AgentDrop inbox for any incoming files."}],
    }],
    betas=["managed-agents-2026-04-01"],
)

# Poll for response (agent handles API calls directly via web_fetch/bash)
import time
while True:
    time.sleep(2)
    state = client.beta.sessions.retrieve(
        session_id=session.id, betas=["managed-agents-2026-04-01"]
    )
    if state.status == "idle":
        events = client.beta.sessions.events.list(
            session_id=session.id, betas=["managed-agents-2026-04-01"]
        )
        for event in events.data:
            if event.type == "agent.message":
                for block in event.content:
                    if block.type == "text":
                        print(block.text)
        break
The agent uses web_fetch or bash (with curl) to hit the AgentDrop API directly. No tool-handling code on your side.
Prototyping only, do not ship this to production.Direct API access via web_fetch / curl skips the AgentDrop SDK entirely, which means you lose:
  • End-to-end encryption: files are uploaded and downloaded as plaintext unless the agent manually implements X25519 + AES-256-GCM.
  • Shield scanning: downloaded files are not scanned for prompt injection or malware before reaching the agent.
  • Key management: your API key lives in the agent’s environment instead of staying in your backend.
For anything past a prototype, use Path 1 (Custom Tools with AgentDropHandler). The handler runs the SDK on your backend so encryption, Shield, and key custody all work correctly.

Custom Tools vs. Direct API

Custom ToolsDirect API
Setup complexityMedium, you write tool handlersLow, system prompt only
Credential securityKeys stay in your backendKeys in agent environment
ReliabilityYou control error handling and retriesAgent handles errors itself
Token efficiencyTool results are structured and conciseAgent writes full API calls (more tokens)
Best forProduction applicationsPrototyping, simple agents

Coming Soon: Native MCP Integration

AgentDrop’s MCP server currently uses stdio transport, which Managed Agents does not support. We are building an HTTP transport adapter that will let Managed Agents connect to AgentDrop natively via MCP, no custom tools or system prompts needed. This will be the simplest integration path once available:
{
  "mcp_servers": [
    {
      "type": "url",
      "name": "agentdrop",
      "url": "https://mcp.agent-drop.com/v1"
    }
  ],
  "tools": [
    {"type": "mcp_toolset", "mcp_server_name": "agentdrop"}
  ]
}
The HTTP MCP endpoint is not available yet. Follow @agentdrop for the announcement.

Shield: File Scanning

AgentDrop Shield scans every downloaded file after decryption, before writing to disk. It’s enabled by default in the handler, no extra setup needed. Shield detects, at a high level:
  • Executables: files that could run code on the recipient’s machine
  • Format mismatches: files where the declared type does not match the actual content (e.g. an executable disguised as a document)
  • Prompt injection: suspicious instructions embedded in text files that appear aimed at the receiving agent
  • Resource abuse: payloads engineered to exhaust disk, memory, or decompression (e.g. zip bombs)
The exact detection rules are not published so they cannot be easily evaded, they ship in the SDK and update with each release. Dangerous files are skipped entirely: they never touch disk. Suspicious files are flagged in the tool result so the agent can decide what to do.

Configuration

handler = AgentDropHandler(
    api_key=os.environ["AGENTDROP_API_KEY"],
    private_key_b64=config["private_key"],
    public_key_b64=config["public_key"],
    agent_id=config["agent_id"],
    # Shield options
    shield=True,                    # enabled by default
    shield_strictness="standard",   # "permissive", "standard", "strict", "paranoid"
    shield_mode="review",           # "review" = skip dangerous, flag suspicious
                                    # "block" = raise error on dangerous files
)
StrictnessUse case
permissiveTrusted internal transfers, only hard-flagged content is skipped
standardGeneral use (default), balanced for typical agent workflows
strictUntrusted sources, more content is flagged for review
paranoidHigh-security environments, maximum sensitivity
The exact scoring thresholds are tuned in the SDK and update with each release as new attack patterns emerge. Shield runs entirely client-side, file contents are never sent to AgentDrop servers for scanning. To disable Shield entirely (not recommended):
handler = AgentDropHandler(..., shield=False)

Tips

  • Bundle files into one transfer. Each transfer counts against your monthly allowance. Sending 10 files as 10 separate transfers wastes 9 transfers.
  • Use short expiry for ephemeral workflows. Set expires_in to 1h or 6h for throwaway transfers to keep storage clean.
  • Set meaningful agent IDs. They appear in transfer logs and make debugging multi-agent workflows much easier.
  • Always call list_sendable first. Verify the recipient is reachable before attempting a transfer. For cross-account transfers, an active connection must exist.
  • Reuse agent IDs across sessions. Once you create an agent, store its ID and reuse it. You do not need to recreate it every time.

What’s Next