Maileditor Docs

Vanilla JS

A static HTML page and an Express route that signs the user id, with no framework at all.

The page cannot hold the Plugin Secret, so a small Node server signs the id and serves the page. The page fetches the signed values and sets the iframe src.

Before you start

Follow the first three steps of the quickstart: create a plugin, add localhost:3000 to its allowed domains, and copy the Plugin ID and Secret.

Configuration

.env
MAILEDITOR_PLUGIN_ID=9f3c1e7a-4b21-4c8e-9a77-1d2f5b6c8e10
MAILEDITOR_PLUGIN_SECRET=your_plugin_secret
MAILEDITOR_EDITOR_BASE_URL=https://maileditor.net
MAILEDITOR_PLUGIN_VERSION=1.0.0

Sign on the server

Run npm init -y, npm install express, and add "type": "module" to package.json.

server.js
import express from 'express'
import { createHmac } from 'node:crypto'

const app = express()
app.use(express.static('public'))

app.get('/api/auth', (req, res) => {
  const user = req.query.user
  if (typeof user !== 'string' || user === '') {
    return res.status(400).json({ error: 'user is required' })
  }

  const expires = Math.floor(Date.now() / 1000) + 300
  const payload = `v1|${Buffer.byteLength(user)}:${user}|${expires}`
  const signature = createHmac('sha256', process.env.MAILEDITOR_PLUGIN_SECRET)
    .update(payload)
    .digest('hex')

  res.json({
    externalUserId: user,
    identityExpires: expires,
    identitySignature: signature,
    pluginId: process.env.MAILEDITOR_PLUGIN_ID,
    pluginVersion: process.env.MAILEDITOR_PLUGIN_VERSION,
    editorBaseUrl: process.env.MAILEDITOR_EDITOR_BASE_URL,
  })
})

app.listen(3000)

Start it with node --env-file=.env server.js and open http://localhost:3000. In a real app, take the user id from the caller's session instead of trusting the user query parameter.

Render the iframe

public/index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Email editor</title>
  </head>
  <body style="margin: 0">
    <iframe
      id="editor"
      title="Email editor"
      allow="clipboard-write; fullscreen"
      style="width: 100%; height: 100vh; border: 0"
    ></iframe>

    <script>
      fetch('/api/auth?user=customer-user-4471')
        .then((res) => res.json())
        .then((auth) => {
          const params = new URLSearchParams({
            external_user_id: auth.externalUserId,
            identity_expires: String(auth.identityExpires),
            identity_signature: auth.identitySignature,
            plugin_version: auth.pluginVersion,
          })
          document.getElementById('editor').src =
            `${auth.editorBaseUrl}/plugin-editor/v1/${auth.pluginId}?${params}`
        })
    </script>
  </body>
</html>

The iframe starts without a src and only loads once the signed values arrive.

Next steps

  • Add template_id to the query string to open one of the user's saved templates. See Embedding the editor.
  • If the iframe shows "Plugin editor unavailable", read result.error in the session response. See Error codes.

On this page