MiN8T
Home

Plugin SDK Developer Guide

Sarah Chen
Sarah Chen
Email Strategy Lead at MiN8T

MiN8T's Plugin SDK lets you embed a full-featured email editor directly into your own application. Your users design emails inside your product, under your brand, without ever knowing MiN8T exists behind the scenes. This guide covers the complete integration path -- from loading the SDK script to production deployment with white-labeling and performance optimization.

The SDK is used by ESP platforms, marketing automation tools, CRM systems, and SaaS products that need email design capabilities without building an editor from scratch. If your product involves emails and your users need to design them, this guide is for you.

5 min
Time to first render
108+
ESP export formats
4
Framework adapters
99.9%
SDK uptime SLA

1 SDK Architecture Overview

The SDK operates as an iframe-based embed. When you initialize the editor, the SDK loads MiN8T's editor application inside a sandboxed iframe within your designated container element. All communication between your host application and the editor happens through a postMessage-based API with typed event contracts.

Why iframe isolation?

Data flow

The typical data flow for an SDK integration:

  1. Your backend generates a short-lived plugin token by calling MiN8T's auth API with your project credentials
  2. Your frontend initializes the SDK with this token and a container element
  3. The SDK loads the editor iframe and authenticates using the token
  4. The user designs their email using the drag-and-drop editor
  5. Your application calls min8t.getHtml() to retrieve the compiled email HTML
  6. Your backend stores or sends the HTML through your own sending infrastructure
i

No vendor lock-in: The HTML output from getHtml() is standard email HTML. It does not contain any MiN8T-specific runtime dependencies. You can send it through any ESP, store it in any database, and render it in any email client. If you stop using MiN8T, your existing emails continue to work.


2 Quick Start Integration

The fastest path to a working editor is three steps: include the SDK script, create a container element, and call init(). Here is a minimal example:

HTML
<!DOCTYPE html>
<html>
<head>
  <script src="https://sdk.min8t.com/v1/editor.js"></script>
</head>
<body>
  <div id="min8t-editor" style="width:100%; height:800px;"></div>

  <script>
    const editor = Min8tEditor.init({
      container: '#min8t-plugin',
      token: 'YOUR_PLUGIN_TOKEN',
      projectId: 'YOUR_PROJECT_ID',
      onReady: function() {
        console.log('Editor loaded and ready');
      }
    });
  </script>
</body>
</html>

That is a fully functional email editor. Your users can drag blocks, edit text, upload images, and design complete email campaigns. When you need the output:

JavaScript
// Get the compiled email HTML
editor.getHtml(function(html) {
  console.log(html); // Full email HTML, ready to send
});

// Get the editor's internal JSON (for save/restore)
editor.getJson(function(json) {
  // Store this JSON to reload the design later
  localStorage.setItem('emailDesign', JSON.stringify(json));
});

// Load a previously saved design
editor.loadJson(JSON.parse(localStorage.getItem('emailDesign')));

// Load from a template ID
min8t.setHtml('template-uuid-here');
!

Token security: Never embed your project secret key in frontend code. The token parameter should be a short-lived plugin token generated by your backend. Plugin tokens expire after 1 hour and are scoped to a single editor session.


3 Framework Patterns

The vanilla JS integration works everywhere, but if you are using a modern framework, the SDK provides adapter patterns that integrate with each framework's lifecycle and state management.

React

JSX
import { useRef, useEffect, useCallback } from 'react';

function EmailEditor({ token, projectId, onSave }) {
  const containerRef = useRef(null);
  const editorRef = useRef(null);

  useEffect(() => {
    if (!containerRef.current || editorRef.current) return;

    editorRef.current = Min8tEditor.init({
      container: containerRef.current,
      token,
      projectId,
      onReady: () => console.log('Editor ready'),
      onChange: () => console.log('Content changed'),
    });

    return () => {
      editorRef.current?.destroy();
      editorRef.current = null;
    };
  }, [token, projectId]);

  const handleSave = useCallback(() => {
    editorRef.current?.getHtml((html) => {
      editorRef.current?.getJson((json) => {
        onSave({ html, json });
      });
    });
  }, [onSave]);

  return (
    <div>
      <div ref={containerRef} style={{ width: '100%', height: '800px' }} />
      <button onClick={handleSave}>Save Email</button>
    </div>
  );
}

Angular

TypeScript
import { Component, ElementRef, ViewChild, OnInit, OnDestroy, Input } from '@angular/core';

@Component({
  selector: 'app-email-editor',
  template: `
    <div #editorContainer style="width:100%; height:800px"></div>
    <button (click)="save()">Save Email</button>
  `
})
export class EmailEditorComponent implements OnInit, OnDestroy {
  @ViewChild('editorContainer', { static: true }) container!: ElementRef;
  @Input() token!: string;
  @Input() projectId!: string;

  private editor: any;

  ngOnInit() {
    this.editor = (window as any).Min8tEditor.init({
      container: this.container.nativeElement,
      token: this.token,
      projectId: this.projectId,
      onReady: () => console.log('Editor ready'),
    });
  }

  save() {
    this.editor.getHtml((html: string) => {
      // Send to your API
    });
  }

  ngOnDestroy() {
    this.editor?.destroy();
  }
}

Vue 3

Vue
<template>
  <div>
    <div ref="editorContainer" style="width:100%; height:800px" />
    <button @click="save">Save Email</button>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';

const props = defineProps(['token', 'projectId']);
const editorContainer = ref(null);
let editor = null;

onMounted(() => {
  editor = window.Min8tEditor.init({
    container: editorContainer.value,
    token: props.token,
    projectId: props.projectId,
    onReady: () => console.log('Editor ready'),
  });
});

function save() {
  editor?.getHtml((html) => {
    // Send to your API
  });
}

onUnmounted(() => editor?.destroy());
</script>

Key pattern: In every framework, the lifecycle is the same: initialize on mount, clean up on unmount. Always call min8t.destroy() in your cleanup to prevent memory leaks and orphaned iframes.


4 Authentication & Security

The SDK uses a two-tier authentication model: project-level credentials for your backend, and session-scoped plugin tokens for the frontend.

Generating plugin tokens

Your backend calls MiN8T's auth endpoint with your project ID and secret key. The response is a short-lived plugin token that you pass to the frontend SDK.

Node.js
// Backend: Generate a plugin token
const response = await fetch('https://api.min8t.com/v1/plugin/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'ES-PLUGIN-AUTH': process.env.MIN8T_PROJECT_SECRET,
  },
  body: JSON.stringify({
    projectId: process.env.MIN8T_PROJECT_ID,
    userId: currentUser.id,        // Your user's ID (for audit trails)
    permissions: ['edit', 'export'], // Scoped permissions
    ttl: 3600,                      // Token lifetime in seconds
  }),
});

const { token } = await response.json();
// Return this token to your frontend

Permission scoping

Plugin tokens can be scoped with granular permissions:

PermissionDescriptionUse Case
editFull editor access: drag-and-drop, text editing, image uploadContent creators, designers
viewRead-only preview of the email designApprovers, reviewers
exportCan call getHtml() and getJson()Automated pipelines
templateCan load and save templatesTemplate library management
uploadCan upload images to the asset CDNControlled by your asset policy
!

Security best practice: Always generate tokens server-side. Never expose your project secret key in frontend code, environment variables accessible to the browser, or client-side configuration files. Rotate your secret key immediately if it is ever exposed.


5 Customization API

The SDK exposes a comprehensive customization API that lets you control the editor's appearance and behavior to match your product.

Toolbar configuration

JavaScript
Min8tEditor.init({
  container: '#editor',
  token: pluginToken,
  projectId: 'your-project-id',

  // Toolbar customization
  toolbar: {
    items: ['text', 'image', 'button', 'divider', 'spacer', 'video'],
    // Hide blocks you don't need:
    hidden: ['social', 'menu', 'html'],
  },

  // Block restrictions
  blocks: {
    maxImages: 10,
    allowHtmlBlock: false,
    allowCustomCode: false,
    imageMaxSize: 2 * 1024 * 1024, // 2MB
    imageAllowedTypes: ['image/jpeg', 'image/png', 'image/gif'],
  },

  // White-labeling
  branding: {
    logo: null,           // null = no logo shown
    poweredBy: false,     // Hide "Powered by MiN8T"
    primaryColor: '#4F46E5',  // Your brand color
    fontFamily: 'Inter, sans-serif',
  },

  // Locale
  locale: 'en',  // Supported: en, es, fr, de, pt, ja, zh, ko

  // Event hooks
  onReady: () => {},
  onChange: (event) => {},
  onSave: (html, json) => {},
  onImageUpload: (file, callback) => {
    // Custom image upload handler
    uploadToYourCdn(file).then(url => callback(url));
  },
  onError: (error) => console.error(error),
});

Custom image upload handler

By default, images uploaded in the editor are stored on MiN8T's CDN. If you want images stored on your own infrastructure, provide an onImageUpload handler:

JavaScript
onImageUpload: async function(file, callback) {
  const formData = new FormData();
  formData.append('image', file);

  const response = await fetch('/api/images/upload', {
    method: 'POST',
    body: formData,
  });

  const { url } = await response.json();
  callback(url); // The editor will use this URL for the image
}

Event hooks reference


6 Performance & Deployment

Loading performance

The SDK script itself is under 15KB (gzipped). The editor application loads asynchronously inside the iframe after init() is called. Typical load times:

MetricValueOptimization
SDK script load~50msServed from global CDN with aggressive caching
Editor iframe load~800msPreloaded assets, code-split bundles
Time to interactive~1.2sProgressive rendering, lazy-load non-critical panels
Subsequent loads~400msService worker caching, HTTP/2 push

Preloading for instant start

If you know the user will open the editor (for example, they are on a "create email" page), you can preload the SDK resources before they click:

JavaScript
// Preload editor resources (call early, before user clicks "create")
Min8tEditor.preload({ projectId: 'your-project-id' });

// Later, when the user clicks "create email":
const editor = Min8tEditor.init({
  container: '#editor',
  token: pluginToken,
  projectId: 'your-project-id',
  // Resources are already cached -- editor loads in ~400ms
});

Versioning strategy

The SDK uses semantic versioning. The script URL includes the major version (/v1/editor.js), which guarantees backward compatibility for all minor and patch updates. Breaking changes only occur on major version bumps, giving you full control over when to migrate.

Deployment checklist: Before going to production, verify: (1) plugin tokens are generated server-side, (2) min8t.destroy() is called on unmount, (3) onError handler reports to your monitoring system, (4) image upload handler points to your production CDN, (5) Content Security Policy allows the MiN8T iframe origin.

Embed a full email editor in your product

Get your Plugin SDK credentials and start integrating in minutes.

Get SDK access