Offline-Capable Email Agents with Jan

Use Jan's open-source desktop AI with MultiMail to build email agents that run locally — with an extension system for email workflows and human oversight.


Jan is an open-source desktop application for running AI models locally with a ChatGPT-like interface, extensions support, and offline capability. MultiMail provides the email infrastructure layer that enables Jan-based agents to send, receive, and manage email while keeping model inference private.

Jan's extension system can integrate MultiMail for email capabilities, creating a hybrid architecture where AI reasoning happens offline and email transport happens through MultiMail's cloud API. The default gated_send mode ensures every email drafted by a local model requires human approval before delivery.

Connect Jan to MultiMail through its OpenAI-compatible API endpoint or by building a custom Jan extension that calls the MultiMail REST API. Both approaches give your local AI email capabilities with human oversight built in.

Built for Jan developers

Offline-First with Cloud Email

Jan runs completely offline for model inference. MultiMail adds cloud email capabilities only when needed, creating a hybrid architecture that balances privacy with functionality.

Extension System Integration

Jan's plugin and extension system can wrap MultiMail's API calls into reusable email tools. Build once and your local AI gains email capabilities through a clean extension interface.

Safety Net for Local Models

Local models may produce inconsistent email quality. MultiMail's oversight modes ensure every outgoing email is reviewed before delivery, catching issues that offline models are more prone to.

Open Source Aligned

Jan is fully open source, and MultiMail provides a transparent API with clear documentation. Both tools align with the open-source philosophy of user control and transparency.

Graduated Trust via Oversight Modes

Start with gated_all (human approves every action) when testing new models in Jan, then progress to gated_send or monitored as you validate the model's email quality.


Get started in minutes

Connect Jan's API to MultiMail
python
from openai import OpenAI
import requests
import json

"cm"># Point OpenAI client at Jan's local server
client = OpenAI(
    base_url="http://localhost:1337/v1",
    api_key="jan"  "cm"># Jan doesn't require a real key
)

MULTIMAIL_API = "https://api.multimail.dev/v1"
MM_HEADERS = {"Authorization": "Bearer mm_live_your_api_key"}

email_tools = [
    {
        "type": "function",
        "function": {
            "name": "send_email",
            "description": "Send an email through MultiMail. In gated_send mode, queues for human approval.",
            "parameters": {
                "type": "object",
                "properties": {
                    "mailbox_id": {"type": "string", "description": "Mailbox to send from"},
                    "to": {"type": "string", "description": "Recipient email"},
                    "subject": {"type": "string", "description": "Subject line"},
                    "body": {"type": "string", "description": "Email body"}
                },
                "required": ["mailbox_id", "to", "subject", "body"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "check_inbox",
            "description": "Check inbox for recent messages.",
            "parameters": {
                "type": "object",
                "properties": {
                    "mailbox_id": {"type": "string", "description": "Mailbox to check"},
                    "limit": {"type": "integer", "description": "Max messages"}
                },
                "required": ["mailbox_id"]
            }
        }
    }
]

Use the OpenAI Python SDK pointed at Jan's local server with MultiMail email tools.

Build an Email Agent with Jan
python
def execute_tool(name, args):
    if name == "send_email":
        resp = requests.post(f"{MULTIMAIL_API}/send", headers=MM_HEADERS, json=args)
    elif name == "check_inbox":
        resp = requests.get(
            f"{MULTIMAIL_API}/mailboxes/{args[&"cm">#039;mailbox_id']}/inbox",
            headers=MM_HEADERS, params={"limit": args.get("limit", 10)}
        )
    elif name == "reply_email":
        resp = requests.post(f"{MULTIMAIL_API}/reply", headers=MM_HEADERS, json=args)
    else:
        return {"error": f"Unknown tool: {name}"}
    return resp.json()

def run_email_agent(user_message, mailbox_id):
    messages = [
        {"role": "system", "content": f"You are an email assistant for mailbox {mailbox_id}. "
         f"Emails use gated_send mode and queue for human approval."},
        {"role": "user", "content": user_message}
    ]
    while True:
        response = client.chat.completions.create(
            model="llama-3.3-70b",  # Use your loaded model name
            messages=messages,
            tools=email_tools
        )
        msg = response.choices[0].message
        if msg.tool_calls:
            messages.append(msg)
            for tc in msg.tool_calls:
                result = execute_tool(
                    tc.function.name,
                    json.loads(tc.function.arguments)
                )
                messages.append({
                    "role": "tool", "tool_call_id": tc.id,
                    "content": json.dumps(result)
                })
        else:
            return msg.content

print(run_email_agent("Check my inbox", "mbx_abc123"))

Create an agentic loop using Jan's OpenAI-compatible API with MultiMail tools.

Jan Extension for MultiMail
typescript
"cm">// Jan extension structure for MultiMail integration
"cm">// Place in ~/.jan/extensions/multimail/

const MULTIMAIL_API = 'https://api.multimail.dev/v1';

interface EmailTool {
  name: string;
  execute: (args: Record<string, unknown>) => Promise<unknown>;
}

const emailTools: EmailTool[] = [
  {
    name: 'send_email',
    execute: async (args) => {
      const resp = await fetch(`${MULTIMAIL_API}/send`, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${getApiKey()}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(args)
      });
      return resp.json();
    }
  },
  {
    name: 'check_inbox',
    execute: async (args) => {
      const mailboxId = args.mailbox_id as string;
      const resp = await fetch(
        `${MULTIMAIL_API}/mailboxes/${mailboxId}/inbox`,
        { headers: { 'Authorization': `Bearer ${getApiKey()}` } }
      );
      return resp.json();
    }
  }
];

function getApiKey(): string {
  "cm">// Read from Jan's secure settings
  return process.env.MULTIMAIL_API_KEY || '';
}

Create a Jan extension that integrates MultiMail email tools into Jan's chat interface.


Step by step

1

Create a MultiMail Account and API Key

Sign up at multimail.dev, create a mailbox, and generate an API key from your dashboard. Your key will start with mm_live_.

2

Install Jan and Download a Model

Download Jan from jan.ai. Use the built-in model hub to download a model with function calling support, such as Llama 3.3.

3

Enable Jan's Local Server

Start Jan's local API server from the settings panel. By default, it runs on localhost:1337 with OpenAI-compatible endpoints.

4

Install Python Dependencies and Build the Agent

Install the OpenAI SDK pointed at Jan's local server and build the agent loop with MultiMail tools.

bash
pip install openai requests
5

Approve Pending Emails

Review and approve pending emails in the MultiMail dashboard. This is especially important with local models that may produce lower-quality outputs.


Common questions

Does Jan support tool calling for email agents?
Jan provides an OpenAI-compatible API that supports function calling with compatible models. Models like Llama 3.3 and Mistral support tool calling through Jan's local server. Check Jan's model hub for models with function calling support.
Can I use Jan completely offline with MultiMail?
Model inference runs offline in Jan, but email delivery requires an internet connection to reach MultiMail's API. You can draft and queue emails while offline and have them sent when connectivity is restored, though this requires custom queuing logic in your agent.
How does Jan's extension system work with MultiMail?
Jan supports extensions that add functionality to its chat interface. You can build a MultiMail extension that registers email tools and handles API calls, making email capabilities available directly within Jan's UI without external scripts.
Why is oversight important for Jan-based email agents?
Local models running in Jan may have weaker instruction following than cloud models. They can produce inappropriate tone, factual errors, or formatting issues in emails. MultiMail's gated_send mode catches these problems before emails reach recipients.
Is there rate limiting on the MultiMail API?
Rate limits depend on your plan tier. The Starter (free) plan allows 200 emails per month, while paid plans range from 5,000 to 150,000. Combined with Jan's zero inference cost, the free tier is ideal for getting started.

Explore more

The only agent email with a verifiable sender

Email infrastructure built for AI agents. Verifiable identity, graduated oversight, and a 38-tool MCP server. Formally verified in Lean 4.