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 requests

Authentication

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 dashboard

Tool 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()

API Reference