Retool as Your AI Email Approval Dashboard

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.

Built for Retool developers

Pending queue is REST-native

MultiMail's `list_pending` endpoint returns structured JSON — subject, body, recipient, agent metadata — that maps directly to Retool table columns with no transformation layer.

Approve or reject in a single API call

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.

Webhook-driven reviewer notifications

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.

Audit trail built in

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.

Per-mailbox oversight mode

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.


Get started in minutes

Fetch the pending email queue
javascript
"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_preview

A Retool REST API query that loads all emails awaiting approval. Add this as a Resource Query against your MultiMail REST resource.

Approve or reject a pending email
javascript
"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.

Load full email body in a reviewer drawer
javascript
"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.

Retool Workflow: notify Slack when queue backs up
javascript
"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.

Send an ad-hoc email from a Retool form
javascript
"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.


Step by step

1

Create a MultiMail REST API resource in Retool

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.

2

Configure your outbound mailbox to gated_send mode

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.

bash
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"}'
3

Build the pending queue table

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.

4

Wire Approve and Reject buttons

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.

5

Add a Retool Workflow for webhook-driven alerts

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.


Common questions

Does this integration require a backend server or middleware?
No. Retool queries MultiMail's REST API directly from its built-in query runner. A REST API resource configured with your Bearer token is sufficient. Retool Workflows handle webhook ingestion without any custom server.
How do I prevent my API key from being visible to Retool app users?
Store your `mm_live_...` key as a Retool environment variable and reference it as `{{ retoolContext.environmentVariables.MULTIMAIL_API_KEY }}` in query headers. Retool encrypts environment variables at rest and does not expose them to end users of your published apps.
What happens if two reviewers try to approve the same email simultaneously?
The first `decide_email` call wins. Subsequent calls on the same email ID return a 409 conflict. Design your Retool app to refresh the table after each decision so reviewers see current queue state and don't act on already-decided emails.
Can I display the full formatted email body, including HTML, in Retool?
Yes. Call GET `/v1/emails/{id}` when a reviewer selects a row to fetch the full email. The response includes `body_html` and `body_text`. Use a Retool HTML component bound to `{{ getEmailDetail.data.body_html }}` to render formatted content inside a side drawer.
How do I record which Retool user approved each email for compliance?
Include `reviewer_id: current_user.email` in the `decide_email` request body. MultiMail records this in the email's event log, retrievable via GET `/v1/emails/{id}/events`. This creates a named chain of custody suitable for SOC 2 audits and GDPR data processing records.
Can emails in the pending queue expire automatically if not reviewed?
Yes. Set `pending_ttl_hours` on the mailbox configuration to automatically expire unreviewed emails after a specified duration. Expired emails are not delivered and their status is recorded as `expired` in the audit log.

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.