Integration Guide
CrewAI
Give your CrewAI agents live outbound postal mail and inbound context forwarded from an address you already use. The assigned-PMB examples below apply only to accounts that already have receiving enabled; they do not provision a new address.
New mailbox.bot-issued physical receiving addresses and associated package receiving: Not live yet · Launch ETA: mid/late September. Reservations are open, but a reservation does not assign or activate an address.
Install
bash
pip install crewai requestsAuthentication
python
import os
API_KEY = os.environ["MAILBOX_BOT_API_KEY"] # sk_agent_*
BASE = "https://mailbox.bot/api/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
AGENT_ID = os.environ["MAILBOX_BOT_AGENT_ID"] # from dashboardTool Definitions
python
from crewai.tools import tool
import requests
# Fetch MAILBOX.md version (required for outbound mail)
def get_md_version() -> str:
r = requests.get(f"{BASE}/agents/{AGENT_ID}/instructions", headers=HEADERS)
return str(r.json()["version"])
MD_VERSION = get_md_version()
@tool("List physical inbound")
def list_physical_inbound() -> str:
"""List letters, flats, and parcels held at the assigned PMB."""
r = requests.get(f"{BASE}/inbound-items", headers=HEADERS)
return str(r.json()) if r.ok else f"Error: {r.text}"
@tool("Request Open & Scan")
def request_open_and_scan(inbound_item_id: str, expected_version: int) -> str:
"""Request the only supported physical action after member approval."""
r = requests.post(
f"{BASE}/inbound-items/{inbound_item_id}/actions",
headers={**HEADERS, "Idempotency-Key": f"open-scan-{inbound_item_id}-{expected_version}"},
json={"action_type": "open_and_scan", "expected_version": expected_version},
)
return str(r.json()) if r.ok else f"Error: {r.text}"
@tool("Get physical scan results")
def get_scan_results(inbound_item_id: str) -> str:
"""Read completed scans for one physical inbound item."""
r = requests.get(f"{BASE}/inbound-items/{inbound_item_id}/scans", headers=HEADERS)
return str(r.json()) if r.ok else f"Error: {r.text}"
@tool("Send a physical letter")
def send_letter(recipient_name: str, recipient_line1: str,
recipient_city: str, recipient_state: str,
recipient_zip: str, pdf_path: str) -> str:
"""Print and mail a PDF letter via USPS. Provide a local path to the PDF.
The facility prints, stuffs an envelope, stamps, and mails it.
Photo proof of mailing is included."""
with open(pdf_path, "rb") as f:
r = requests.post(
f"{BASE}/mail",
headers={**HEADERS, "X-Mailbox-MD-Version": MD_VERSION},
files={"document": ("letter.pdf", f, "application/pdf")},
data={
"recipient_name": recipient_name,
"recipient_line1": recipient_line1,
"recipient_city": recipient_city,
"recipient_state": recipient_state,
"recipient_zip": recipient_zip,
"mail_class": "first_class",
},
)
if r.status_code == 201:
return f"Letter queued: {r.json()['outbound_mail']['id']}"
return f"Error: {r.text}"
Agent and Crew Setup
python
from crewai import Agent, Task, Crew
# Define an agent with mail capabilities
mail_clerk = Agent(
role="Mail Operations Clerk",
goal="Review physical inbound items and send approved outbound correspondence",
backstory="You keep forwarded digital/OCR context separate from physical custody. "
"For assigned-PMB items, Open & Scan is the only callable action and "
"requires the current item version plus member approval.",
tools=[list_physical_inbound, request_open_and_scan, get_scan_results, send_letter],
verbose=True,
)
# Define a task
daily_mail_check = Task(
description="""List physical inbound items at the assigned PMB.
Report sender and item kind. Request Open & Scan only after the member
approves and only with the current item version. Infer no other action.""",
expected_output="Summary of all mail processed with actions taken",
agent=mail_clerk,
)
# Run the crew
crew = Crew(agents=[mail_clerk], tasks=[daily_mail_check], verbose=True)
result = crew.kickoff()
print(result)Multi-Agent Crew Example
For an account that already has an assigned PMB and receiving enabled, combine the account-enabled REST tools with other agents in a crew. The mail clerk handles physical correspondence while other agents handle the cognitive work.
python
from crewai import Agent, Task, Crew
# Mail clerk handles postal mail
mail_clerk = Agent(
role="Mail Clerk",
goal="Review physical inbound and extract approved scan information",
tools=[list_physical_inbound, request_open_and_scan, get_scan_results],
verbose=True,
)
# Document analyst reviews scanned documents
document_analyst = Agent(
role="Document Analyst",
goal="Review scanned documents, identify deadlines, "
"and draft appropriate responses when approved",
tools=[send_letter], # can send response letters
verbose=True,
)
# Tasks with handoff
triage = Task(
description="List assigned-PMB items. Request Open & Scan only after approval. "
"Report completed scans and infer no other physical action.",
expected_output="List of scanned documents with OCR text",
agent=mail_clerk,
)
review = Task(
description="Review the scanned documents. Extract deadlines. "
"If a response is required, draft it, run a dry run, "
"and request approval before live mail.",
expected_output="Summary of actions taken",
agent=document_analyst,
context=[triage], # receives output from triage
)
crew = Crew(agents=[mail_clerk, document_analyst], tasks=[triage, review])
result = crew.kickoff()