Skip to content
Writing
TutorialAI agentsLangChainCrewAI

How to Give an AI Agent Email Access Without Handing Over Your Whole Account

Step-by-step tutorial on minting a scoped agent key, setting up domain allowlists, and integrating email tools into LangChain, CrewAI, and TypeScript loops safely.

Tayyab MughalFounder & AI Chief8 min read

The master key anti-pattern

The most common security mistake in AI agent development is pasting a master production API key into an agent’s environment variables. If the agent gets stuck in a loop, it drains your quota; if it encounters a prompt injection in a customer ticket, an attacker can command it to email anyone from your brand.

To give an agent email access safely, you must enforce the principle of least privilege at the API gateway.

Step 1: Mint a scoped agent key with an allowlist

Create a dedicated API key constrained strictly to sending, with an allowlist limited to internal test recipients or your corporate domain:

# Mint an agent key via the SadaSend CLI or dashboard
sadasend keys create \
  --name "customer-triage-agent" \
  --scopes "send,read" \
  --allowlist "@yourcompany.com" \
  --rate-limit 30

Step 2: Connecting to LangChain (Python)

Here is how to create a custom LangChain tool using the SadaSend SDK with built-in error handling for allowlist violations:

from langchain.tools import tool
import requests

@tool
def send_agent_email(to_email: str, subject: str, body: str) -> str:
    """Send an email to a customer or internal stakeholder safely."""
    response = requests.post(
        "https://api.sadasend.com/v1/emails",
        headers={"Authorization": "Bearer ssk_live_agent_key_xyz"},
        json={
            "from": "triage-bot@yourdomain.com",
            "to": to_email,
            "subject": subject,
            "text": body,
        }
    )
    if response.status_code == 200:
        return f"Email sent successfully. ID: {response.json()['id']}"
    elif response.status_code == 403:
        return f"Guardrail refused send: {response.json()['message']}"
    else:
        return f"Error sending email: {response.text}"

Step 3: Connecting to CrewAI or TypeScript Agent Loops

In TypeScript agent architectures, initialize the SadaSend client with your scoped key:

import { SadaSend } from '@sadasend/sdk';

const sadasend = new SadaSend({ apiKey: process.env.SADASEND_AGENT_KEY });

async function executeAgentOutbound(recipient: string, subject: string, markdown: string) {
  try {
    const { id } = await sadasend.emails.send({
      from: 'triage@yourdomain.com',
      to: recipient,
      subject: subject,
      markdown: markdown,
    });
    console.log(`Dispatched message ${id}`);
  } catch (err: any) {
    if (err.code === 'refused_recipient_outside_allowlist') {
      console.warn('Agent tried to email outside allowed domain:', recipient);
    }
  }
}

Step 4: Dry-run verification before production launch

Set your agent key to `dry-run` mode in your staging environment. The API will validate DNS, DKIM alignment, and allowlists, but drop the message before it hits external mail servers — giving you full confidence in CI/CD.