Intercom handles conversational support. MultiMail adds the approval gating, identity verification, and audit trail your AI agents need before sending email outside the thread.
Intercom's Fin AI agent and custom bots are effective at resolving support conversations — but when an agent needs to send a follow-up email outside the Intercom thread, you lose the oversight controls your team depends on. The message leaves through a shared mailbox with no approval step, no cryptographic proof of origin, and no audit trail.
MultiMail fills that gap. By routing agent-authored outbound email through MultiMail's REST API, you get per-message approval gating, DKIM-signed delivery from a verified sender identity, and a complete decision log — all without changing how Intercom handles inbound conversations.
The integration is a webhook-to-API bridge: Intercom fires a webhook when an agent drafts an outbound message, your backend calls MultiMail's `send_email` or `decide_email` endpoints, and the approval state flows back to Intercom via its Conversations API. Your human team approves in MultiMail's queue; the message sends only after explicit sign-off.
This pattern applies to any Intercom workflow that touches outbound email: churn-risk escalations drafted by Fin, onboarding sequences triggered by lifecycle events, or proactive outreach composed by a custom agent reading CRM signals.
Intercom's native outbound email has no per-message approval step for AI-authored content. MultiMail's `gated_send` mode holds every agent-drafted message in a review queue until a human approves it via the API or dashboard — then delivers it with a cryptographic signature proving the approval happened.
Messages sent through MultiMail carry a DKIM signature tied to your verified domain and an `X-MultiMail-Agent` header identifying the agent that authored the message. Intercom's shared mailbox provides neither. This matters for CAN-SPAM compliance and for recipients who want to verify the message came from a real, accountable source.
Every `decide_email` call — approve or reject — is written to an append-only audit log with timestamp, approver identity, and the original draft. If a regulator, customer, or internal reviewer asks what your AI sent and who authorized it, you have a complete answer.
Use `create_mailbox` to provision dedicated addresses like `[email protected]` or `[email protected]`. Replies route back through MultiMail's inbound pipeline, keeping agent-originated threads separate from your human support queue in Intercom.
Intercom's workflow automation fires webhooks on conversation events. MultiMail's inbound webhooks handle delivery status, approval events, and replies — so the two platforms compose without polling loops or manual reconciliation.
import httpx
MULTIMAIL_API_KEY = "mm_live_your_key_here"
BASE_URL = "https://api.multimail.dev"
def send_agent_followup(conversation_id: str, customer_email: str, draft_body: str) -> dict:
"""
Called from an Intercom webhook handler after Fin resolves a conversation.
Routes the follow-up email through MultiMail with gated_send oversight.
"""
payload = {
"from": "[email protected]",
"to": customer_email,
"subject": "Following up on your support request",
"body": draft_body,
"oversight_mode": "gated_send",
"metadata": {
"source": "intercom",
"conversation_id": conversation_id,
"agent": "fin"
}
}
response = httpx.post(
f"{BASE_URL}/send_email",
json=payload,
headers={"Authorization": f"Bearer {MULTIMAIL_API_KEY}"}
)
response.raise_for_status()
result = response.json()
"cm"># result["message_id"] can be stored against conversation_id
"cm"># for later approval status polling or webhook correlation
return result
When Intercom's Fin agent resolves a conversation and wants to send a follow-up email, call MultiMail's send_email endpoint with gated_send oversight. The message is held until a human approves it.
import httpx
from typing import Literal
MULTIMAIL_API_KEY = "mm_live_your_key_here"
BASE_URL = "https://api.multimail.dev"
def get_pending_messages() -> list[dict]:
response = httpx.get(
f"{BASE_URL}/list_pending",
headers={"Authorization": f"Bearer {MULTIMAIL_API_KEY}"}
)
response.raise_for_status()
return response.json()["messages"]
def review_message(
message_id: str,
decision: Literal["approve", "reject"],
reviewer: str,
note: str = ""
) -> dict:
"""
Approve or reject an agent-drafted outbound email.
On approve, MultiMail delivers the message and signs it with DKIM.
On reject, the message is cancelled and logged with the reviewer&"cm">#039;s note.
"""
response = httpx.post(
f"{BASE_URL}/decide_email",
json={
"message_id": message_id,
"decision": decision,
"reviewer": reviewer,
"note": note
},
headers={"Authorization": f"Bearer {MULTIMAIL_API_KEY}"}
)
response.raise_for_status()
return response.json()
# Example: approve the first pending message
pending = get_pending_messages()
if pending:
result = review_message(
message_id=pending[0]["message_id"],
decision="approve",
reviewer="[email protected]"
)
print(result["status"]) # "delivered"
Use list_pending to surface agent-drafted messages awaiting approval, then decide_email to approve or reject. This can be called from an internal admin tool or wired to a Slack command.
import express from 'express';
import crypto from 'crypto';
const app = express();
app.use(express.json());
const MULTIMAIL_API_KEY = process.env.MULTIMAIL_API_KEY;
const INTERCOM_SECRET = process.env.INTERCOM_CLIENT_SECRET;
const MULTIMAIL_BASE = 'https://api.multimail.dev';
"cm">// Verify Intercom webhook signature
function verifyIntercomSignature(rawBody, signature) {
const digest = crypto
.createHmac('sha1', INTERCOM_SECRET)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(`sha1=${digest}`),
Buffer.from(signature)
);
}
app.post('/webhooks/intercom', express.raw({ type: 'application/json' }), async (req, res) => {
const sig = req.headers['x-hub-signature'];
if (!verifyIntercomSignature(req.body, sig)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(req.body);
if (event.topic === 'conversation.resolved' && event.data?.item?.source?.author?.type === 'bot') {
const conversation = event.data.item;
const customerEmail = conversation.contacts?.contacts?.[0]?.email;
const summary = conversation.source?.body ?? '';
if (customerEmail && summary) {
const response = await fetch(`${MULTIMAIL_BASE}/send_email`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${MULTIMAIL_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
from: '[email protected]',
to: customerEmail,
subject: 'Your support case has been resolved',
body: summary,
oversight_mode: 'gated_send',
metadata: {
source: 'intercom',
conversation_id: conversation.id
}
})
});
const result = await response.json();
console.log('Queued for approval:', result.message_id);
}
}
res.json({ received: true });
});
app.listen(3000);
A minimal Express.js handler that receives Intercom conversation-resolved webhooks and queues the follow-up email in MultiMail for human approval.
import httpx
MULTIMAIL_API_KEY = "mm_live_your_key_here"
BASE_URL = "https://api.multimail.dev"
def provision_bot_mailbox(alias: str, display_name: str) -> dict:
"""
Creates a dedicated mailbox for Intercom AI agent outbound email.
Replies to this address route through MultiMail&"cm">#039;s inbound pipeline.
"""
response = httpx.post(
f"{BASE_URL}/create_mailbox",
json={
"address": f"{alias}@multimail.dev",
"display_name": display_name,
"oversight_mode": "gated_send",
"webhook_url": "https://yourapp.com/webhooks/multimail"
},
headers={"Authorization": f"Bearer {MULTIMAIL_API_KEY}"}
)
response.raise_for_status()
return response.json()
mailbox = provision_bot_mailbox(
alias="intercom-bot",
display_name="Support Bot (via Intercom)"
)
print(mailbox["address"]) # [email protected]
print(mailbox["mailbox_id"]) "cm"># use this when calling send_email
Create a dedicated outbound address for your Intercom AI agent so replies route separately from your human support queue.
Sign up at multimail.dev and generate a live API key (mm_live_...) from the dashboard. Use a test key (mm_test_...) during development — test mode queues messages but never delivers them.
"cm"># Verify your key works
curl -s https://api.multimail.dev/list_pending \
-H "Authorization: Bearer $MULTIMAIL_API_KEY" | jq .pending_count
Use create_mailbox to give your Intercom AI agent its own address. This keeps bot-originated threads separate from your human support queue and routes replies back through MultiMail.
curl -s -X POST https://api.multimail.dev/create_mailbox \
-H "Authorization: Bearer $MULTIMAIL_API_KEY" \
-H "Content-Type: application/json" \
-d &"cm">#039;{
"address": "[email protected]",
"display_name": "Support Bot",
"oversight_mode": "gated_send",
"webhook_url": "https://yourapp.com/webhooks/multimail"
}&"cm">#039; | jq .mailbox_id
In Intercom's Developer Hub, subscribe to conversation.resolved and conversation.created events. Point them at your backend handler, which validates the X-Hub-Signature header and calls MultiMail's send_email endpoint with the bot-drafted content and oversight_mode set to gated_send.
Call list_pending periodically (or on webhook trigger) to surface messages awaiting review. Your team approves or rejects via decide_email. On approval, MultiMail delivers the message and fires your outbound webhook with the delivery receipt.
"cm"># List pending approvals
curl -s https://api.multimail.dev/list_pending \
-H "Authorization: Bearer $MULTIMAIL_API_KEY" | jq &"cm">#039;.messages[] | {id: .message_id, to: .to, subject: .subject}'
"cm"># Approve a specific message
curl -s -X POST https://api.multimail.dev/decide_email \
-H "Authorization: Bearer $MULTIMAIL_API_KEY" \
-H "Content-Type: application/json" \
-d &"cm">#039;{"message_id": "msg_abc123", "decision": "approve", "reviewer": "[email protected]"}'
After approving a message, use read_email with the message_id to confirm delivery status, DKIM signature verification, and the approver record. This is your evidence trail for CAN-SPAM compliance and internal audits.
curl -s https://api.multimail.dev/read_email \
-G --data-urlencode "message_id=msg_abc123" \
-H "Authorization: Bearer $MULTIMAIL_API_KEY" \
| jq &"cm">#039;{status: .status, delivered_at: .delivered_at, approved_by: .decision.reviewer, dkim: .dkim_verified}'
Email infrastructure built for AI agents. Verifiable identity, graduated oversight, and a 38-tool MCP server. Formally verified in Lean 4.