Embed MiN8T's Email Editor in Your Product: A Step-by-Step Guide
1 Overview
The MiN8T Plugin SDK lets you embed a full drag-and-drop email editor directly inside your own application. Your users design emails without ever leaving your product -- no redirects, no iframe hacks, no "powered by" badges unless you want them.
This is how SaaS platforms add email design to their feature set without building an editor from scratch. Marketing automation tools, CRM platforms, agency dashboards, e-commerce backends, and enterprise communication suites all use embedded editors to give their users a native email creation experience.
With the Plugin SDK, you get the full MiN8T editing experience -- drag-and-drop blocks, responsive design controls, image management, HTML and AMP output -- rendered inside a container element you control. You decide where the editor appears, what tools are available, which templates users can access, and how the final HTML gets saved.
Not an iframe. The Plugin SDK renders the editor natively in your DOM. This means full control over sizing, styling, and interaction. The editor adapts to your layout, not the other way around.
2 What You'll Need
Before you start, make sure you have these three things ready:
- A MiN8T account on the Startup plan or above. The Plugin SDK is available on Startup, Business, and Enterprise tiers. Free accounts do not include SDK access. If you are on the Free plan, upgrade from Settings → Billing.
- A web application. The SDK works with React, Angular, Vue, Svelte, or plain HTML/JavaScript. Any framework that can render a
<div>and load a script tag will work. - Your Plugin SDK credentials. You will need a Project ID and an API Token, both generated from Developer Tools inside your MiN8T dashboard. We will create these in Step 1.
Keep your API Token secret. The token should never appear in client-side code that ships to browsers. Generate short-lived session tokens on your server and pass those to the frontend. We cover this pattern in the Going Further section.
3 Step 1: Create a Project
Every embedded editor instance belongs to a project. A project ties together your credentials, allowed origins, and usage quotas.
- Log in to your MiN8T dashboard
- Navigate to Settings → Developer Tools → Projects
- Click Create Project
- Enter a name for your project (e.g., "My SaaS Email Editor")
- Add your allowed origins -- the domains where the editor will load (e.g.,
https://yourapp.com,http://localhost:3000for development) - Click Create
After creation, MiN8T displays your Project ID and API Token. Copy both and store them securely. The API Token is shown once -- if you lose it, you can regenerate it from the project settings, but the old token will stop working immediately.
Separate projects for environments. Create one project for development (with http://localhost origins) and another for production. This keeps your production token isolated and lets you test without affecting live usage quotas.
4 Step 2: Install the SDK
You can install the SDK via npm or load it from the CDN. Choose whichever fits your build process.
Option A: npm
npm install @min8t.com/plugin-sdk
Option B: CDN script tag
Add this to your HTML, ideally just before the closing </body> tag:
<script src="https://cdn.min8t.com/plugin-sdk/v1/min8t-sdk.js"></script>
The CDN version exposes a global Min8t object on window. The npm package provides named exports. Both give you the same API -- the only difference is how you import it.
5 Step 3: Initialize the Editor
Create a container element in your HTML, then call Min8t.init() to mount the editor inside it. Here is the minimal working example:
<!-- Container where the editor will render -->
<div id="min8t-editor" style="width: 100%; height: 800px;"></div>
import { Min8t } from '@min8t.com/plugin-sdk';
const editor = await Min8t.init({
projectId: 'proj_abc123def456',
token: 'YOUR_SESSION_TOKEN',
container: '#min8t-plugin',
locale: 'en',
});
// The editor is now live and ready for user interaction.
That is it -- ten lines including the container div. The init() call returns a promise that resolves to an editor instance. The editor loads inside your container, respecting whatever dimensions you set via CSS.
Responsive sizing: Set the container to width: 100%; height: 100vh; for a full-page editor, or constrain it to a specific region of your layout. The editor adapts to any size above 320px wide.
6 Step 4: Load a Template
Once the editor is initialized, you can load a blank canvas or an existing template. By default, the editor starts with a blank email. To load a specific template:
// Load a template by ID (from your MiN8T template library)
await min8t.setHtml('tpl_a1b2c3d4e5f6');
// Or load from raw HTML + JSON
await min8t.setHtml({
html: '<html>...your email HTML...</html>',
json: { /* MiN8T editor JSON structure */ }
});
// Start with a blank template explicitly
await min8t.setHtml('blank');
The loadTemplate() method accepts either a string template ID (which fetches the template from the MiN8T API) or an object with html and json properties (which loads the design directly without a network request). The JSON structure is the same format returned by min8t.compile(), so you can save it to your own database and reload it later.
Template library access: Templates loaded by ID must belong to the same account that owns the project. You can use this to offer your users a curated set of starter templates without exposing your full template library.
7 Step 5: Save and Export
When your user finishes editing, you need to extract the result. The editor provides two output formats: compiled HTML (ready to send) and JSON (the editor's internal format, for reloading later).
// Get the compiled HTML -- ready to send via any ESP
const html = await min8t.getHtml();
// Get the JSON design -- save this to reload the design later
const json = await min8t.compile();
// Save both to your backend
await fetch('/api/emails/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
emailId: currentEmailId,
html: html,
design: json,
}),
});
The HTML output includes inlined CSS, responsive media queries, and is optimized for email client compatibility. The JSON output is a structured representation of the design that the editor can reload with full fidelity -- all block positions, styles, content, and settings are preserved.
Always save both. If you only save the HTML, you lose the ability to re-edit the design. The HTML is a one-way compiled output. Store the JSON alongside the HTML so users can pick up where they left off.
8 Step 6: Customize the Editor
The editor's appearance and available tools are fully configurable. Pass options to Min8t.init() to tailor the experience for your users.
const editor = await Min8t.init({
projectId: 'proj_abc123def456',
token: 'YOUR_SESSION_TOKEN',
container: '#min8t-plugin',
// Toolbar: show only the tools your users need
toolbar: {
blocks: true, // Drag-and-drop content blocks
images: true, // Image upload and library
structures: true, // Layout structures (1-col, 2-col, etc.)
settings: true, // Email-wide settings (width, background)
preview: true, // Desktop/mobile preview toggle
undo: true, // Undo/redo buttons
codeEditor: false, // Hide the raw HTML editor
},
// Brand: lock colors and fonts to your client's brand
brand: {
colors: ['#0066FF', '#00CC88', '#FF6600', '#333333', '#FFFFFF'],
fonts: ['Inter', 'Georgia', 'Arial'],
defaultFont: 'Inter',
},
// Content blocks: add your own custom blocks
customBlocks: [
{
id: 'product-card',
label: 'Product Card',
icon: 'https://yourapp.com/icons/product.svg',
html: '<div class="product">...</div>',
},
],
// Locking: restrict what users can edit
locked: {
header: true, // Lock the header block (no edits)
footer: true, // Lock the footer block
deleteBlocks: false, // Allow deleting non-locked blocks
},
});
The toolbar object toggles individual tools on and off. The brand object restricts the color picker and font dropdown to your approved palette -- users can still design freely, but every choice stays within brand guidelines. The locked object prevents modifications to specific regions, which is useful for enforcing consistent headers and footers across all emails.
Custom blocks are powerful. You can create product cards, pricing tables, social media footers, or any reusable component as a custom block. Users drag them from the sidebar just like built-in blocks, but the HTML is yours.
9 Step 7: Handle Events
The editor emits events as the user works. Hook into these to sync changes with your backend, trigger autosave, show notifications, or integrate with your app's state management.
// Fires every time the design changes (debounced, ~300ms)
min8t.on('change', async (event) => {
console.log('Design modified:', event.timestamp);
// Trigger autosave
const json = await min8t.compile();
saveDraft(json);
});
// Fires when the user clicks "Save" in the editor toolbar
min8t.on('save', async (event) => {
const html = await min8t.getHtml();
const json = await min8t.compile();
await saveToBackend(html, json);
showNotification('Email saved successfully');
});
// Fires when the user triggers an export
min8t.on('export', (event) => {
console.log('Exported to:', event.target);
console.log('HTML size:', event.htmlSize, 'bytes');
});
// Fires on errors (failed image upload, network timeout, etc.)
min8t.on('error', (event) => {
console.error('Editor error:', event.code, event.message);
showErrorToast(event.message);
});
// Fires when the editor finishes loading
min8t.on('ready', () => {
hideLoadingSpinner();
});
Events follow a standard pattern: call min8t.on(eventName, callback) to subscribe and editor.off(eventName, callback) to unsubscribe. The change event is debounced internally at 300ms so you are not overwhelmed during rapid edits.
| Event | When It Fires | Payload |
|---|---|---|
ready | Editor fully loaded and interactive | None |
change | Any design modification (debounced) | { timestamp } |
save | User clicks Save | { timestamp } |
export | Export completed | { target, htmlSize } |
error | Something went wrong | { code, message } |
templateLoaded | Template finished loading | { templateId } |
destroy | Editor instance destroyed | None |
10 Going Further
Once you have the basics working, here are the next areas to explore.
White-labeling
On the Business and Enterprise plans, you can fully remove MiN8T branding from the editor. Pass whiteLabel: true in the init options. You can also inject your own logo into the editor toolbar using the logo option:
const editor = await Min8t.init({
projectId: 'proj_abc123def456',
token: 'YOUR_SESSION_TOKEN',
container: '#min8t-plugin',
whiteLabel: true,
logo: 'https://yourapp.com/logo.svg',
});
Server-side token generation
Never expose your API Token in client-side code. Instead, create a token endpoint on your server that exchanges your API Token for a short-lived session token:
// Server endpoint: POST /api/editor-token
app.post('/api/editor-token', authenticate, async (req, res) => {
const response = await fetch('https://api.min8t.com/v1/plugin/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'ES-PLUGIN-AUTH': process.env.MIN8T_API_TOKEN,
},
body: JSON.stringify({
projectId: process.env.MIN8T_PROJECT_ID,
userId: req.user.id, // Your app's user ID
expiresIn: '1h', // Token lifetime
}),
});
const { sessionToken } = await response.json();
res.json({ token: sessionToken });
});
// Client-side: fetch token before initializing
const { token } = await fetch('/api/editor-token', { method: 'POST' }).then(r => r.json());
const editor = await Min8t.init({
projectId: 'proj_abc123def456',
token: token,
container: '#min8t-plugin',
});
Framework-specific examples
The SDK works the same way in every framework -- the only difference is where you call init() and destroy():
- React: Call
Min8t.init()inside auseEffecthook with a ref for the container. Returnmin8t.destroy()from the cleanup function. - Angular: Initialize in
ngAfterViewInit()with a@ViewChildreference. Destroy inngOnDestroy(). - Vue: Initialize in
onMounted()with a template ref. Destroy inonUnmounted().
import { useEffect, useRef } from 'react';
import { Min8t } from '@min8t.com/plugin-sdk';
function EmailEditor({ token, templateId }) {
const containerRef = useRef(null);
useEffect(() => {
let editor;
async function initEditor() {
editor = await Min8t.init({
projectId: 'proj_abc123def456',
token,
container: containerRef.current,
});
if (templateId) await min8t.setHtml(templateId);
}
initEditor();
return () => editor?.destroy();
}, [token, templateId]);
return <div ref={containerRef} style={{ width: '100%', height: '100vh' }} />;
}
Rate limits and quotas
SDK usage counts toward your plan's quotas. Here are the key limits to be aware of:
| Plan | Active Sessions | API Calls / min | Storage |
|---|---|---|---|
| Startup | 50 concurrent | 300 | 5 GB |
| Business | 500 concurrent | 1,500 | 50 GB |
| Enterprise | Unlimited | Custom | Custom |
If you exceed the concurrent session limit, new init() calls return an error with code QUOTA_EXCEEDED. Handle this gracefully in your error handler to show users a retry message.
Monitor usage: The Developer Tools dashboard shows real-time session counts, API call volume, and storage consumption. Set up alerts to get notified before you hit a limit.
Embed MiN8T in your product today
Get your SDK credentials and ship an email editor in under an hour.
Get started free