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 30Step 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.