Wire MultiMail's pending queue into a Retool app and give your operations team a clean interface to review, approve, or reject AI-drafted email before it sends.
Retool is where operations teams build the internal tools that run their business — dashboards, approval queues, and workflow forms layered on top of APIs and databases. When you add AI agents to that picture, Retool becomes the natural surface for humans to stay in the loop.
MultiMail's graduated oversight modes let AI agents draft and queue outbound email without sending autonomously. The `gated_send` and `gated_all` modes hold email in a pending queue until a human approves. Retool can query that queue, surface full email content and agent metadata, and let reviewers approve or reject with a single click.
The integration requires no custom backend. Retool queries MultiMail's REST API directly. A resource pointing to `https://api.multimail.dev` with your Bearer token is all the infrastructure you need — no proxy, no Lambda, no middleware.
MultiMail's `list_pending` endpoint returns structured JSON — subject, body, recipient, agent metadata — that maps directly to Retool table columns with no transformation layer.
The `decide_email` endpoint accepts `approve` or `reject` with an optional rejection reason. Wire it to a Retool button and the queue drains without any custom middleware.
MultiMail fires webhooks when new emails enter the pending queue. A Retool Workflow can receive those events and post to Slack or trigger in-app notifications so reviewers don't have to poll.
Every approve and reject is logged with a timestamp and reviewer identity. MultiMail surfaces this through the email events API, giving you a complete chain of custody suitable for SOC 2 and GDPR data processing reviews.
Different mailboxes can run different oversight modes. High-stakes outbound — sales, legal, external partners — stays in `gated_send`. Internal notifications can run `monitored` or `autonomous`. Retool surfaces only what needs human review.
"cm">// Retool REST API Query
"cm">// Resource: MultiMail (Base URL: https://api.multimail.dev)
"cm">// Method: GET
"cm">// Endpoint: /v1/pending
"cm">// Headers: Authorization: Bearer {{ retoolContext.environmentVariables.MULTIMAIL_API_KEY }}
"cm">// URL params:
"cm">// limit: 50
"cm">// mailbox: [email protected]
"cm">// Bind to a Table component:
"cm">// data source: {{ getPendingEmails.data.emails }}
"cm">// columns: id, to, subject, created_at, metadata.agent_id, body_previewA Retool REST API query that loads all emails awaiting approval. Add this as a Resource Query against your MultiMail REST resource.
"cm">// Retool JavaScript Query — triggered by button onClick
const emailId = table1.selectedRow.data.id;
const decision = self.name === 'approveBtn' ? 'approve' : 'reject';
const reason = decision === 'reject' ? rejectReasonInput.value : undefined;
const response = await fetch(
`https:"cm">//api.multimail.dev/v1/emails/${emailId}/decide`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${retoolContext.environmentVariables.MULTIMAIL_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
decision,
reason,
reviewer_id: current_user.email
})
}
);
if (!response.ok) {
const err = await response.json();
throw new Error(`decide_email failed: ${err.error}`);
}
"cm">// Refresh the queue table
await getPendingEmails.trigger();A Retool JavaScript query triggered by Approve and Reject buttons. Reads the selected row ID and posts the reviewer's decision to MultiMail.
"cm">// Retool REST API Query — triggered by table1.onRowClick
"cm">// Method: GET
"cm">// Endpoint: /v1/emails/{{ table1.selectedRow.data.id }}
"cm">// Headers: Authorization: Bearer {{ retoolContext.environmentVariables.MULTIMAIL_API_KEY }}
"cm">// In the Drawer component, bind:
"cm">// Text: emailSubject → {{ getEmailDetail.data.subject }}
"cm">// Text: emailTo → {{ getEmailDetail.data.to }}
"cm">// HTML: emailBody → {{ getEmailDetail.data.body_html }}
"cm">// Text: agentId → {{ getEmailDetail.data.metadata.agent_id }}
"cm">// Text: sentAt → {{ getEmailDetail.data.created_at }}When a reviewer clicks a row in the pending table, load the complete email content using the read_email endpoint and display it in a Drawer component.
"cm">// Retool Workflow — Start Trigger: every 5 minutes
"cm">// Step 1: Query MultiMail pending count
const resp = await fetch(
'https://api.multimail.dev/v1/pending?limit=1',
{ headers: { 'Authorization': `Bearer ${workflow.environment.MULTIMAIL_API_KEY}` } }
);
const { total_pending } = await resp.json();
"cm">// Step 2: Branch — skip if queue is healthy
if (total_pending < 5) return { skipped: true, total_pending };
"cm">// Step 3: Notify via Retool Slack resource
await slackResource.postMessage({
channel: '#ops-email-review',
text: `*MultiMail approval queue*: ${total_pending} emails waiting.\n` +
`<https:"cm">//acme.retool.com/apps/email-review|Open review app>`
});A Retool Workflow on a cron schedule that polls MultiMail and alerts the team when pending emails accumulate beyond a threshold.
"cm">// Retool JavaScript Query — triggered by form submit button
const payload = {
from: '[email protected]',
to: recipientInput.value,
subject: subjectInput.value,
body: bodyInput.value,
tags: ['manual', 'ops-initiated']
};
const response = await fetch('https://api.multimail.dev/v1/emails/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${retoolContext.environmentVariables.MULTIMAIL_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
const result = await response.json();
if (result.id) {
utils.showNotification({
title: 'Sent',
description: `Message ID: ${result.id}`,
notificationType: 'success'
});
form1.reset();
} else {
utils.showNotification({
title: 'Failed',
description: result.error,
notificationType: 'error'
});
}Allow ops staff to send email directly through MultiMail from a Retool form, bypassing the AI agent pipeline for manual customer communications.
In Retool, go to Resources → Create New → REST API. Set Base URL to `https://api.multimail.dev` and add a default header: `Authorization: Bearer {{ retoolContext.environmentVariables.MULTIMAIL_API_KEY }}`. Store your `mm_live_...` key as a Retool environment variable so it is encrypted at rest and not exposed to app end users.
Patch your mailbox to hold outbound email for review rather than delivering immediately. Agents writing to this mailbox will have their messages queued until a Retool reviewer approves.
curl -X PATCH https://api.multimail.dev/v1/mailboxes/[email protected] \
-H &"cm">#039;Authorization: Bearer $MULTIMAIL_API_KEY...' \
-H &"cm">#039;Content-Type: application/json' \
-d &"cm">#039;{"oversight_mode": "gated_send"}'Create a Retool app with a Table component. Add a REST API query to your MultiMail resource: GET `/v1/[email protected]`. Set the table's data source to `{{ getPendingEmails.data.emails }}` and add columns for id, to, subject, created_at, and body_preview.
Add two buttons below the table. For Approve, create a JavaScript query that calls `POST /v1/emails/{{ table1.selectedRow.data.id }}/decide` with `{ decision: 'approve', reviewer_id: current_user.email }`. For Reject, include `{ decision: 'reject', reason: rejectInput.value }`. On success, trigger `getPendingEmails` to refresh the queue.
Create a Retool Workflow with a Webhook trigger. Register the generated webhook URL in MultiMail's dashboard for the `email.pending` event type. The workflow fires when a new email enters the queue and can notify your team via Slack or email before they open the review app.
Email infrastructure built for AI agents. Verifiable identity, graduated oversight, and a 38-tool MCP server. Formally verified in Lean 4.