Skip to content

1. Get an API key

Sign up at app.maillune.com and create an API key from the Keys page.

Keep your API key on the server

Your API key grants full access to your account. Never put it in client-side code, a public env var, or the browser DOM. Use short-lived session tokens for the browser SDK — see step 3.

2. Install

bash
npm install @maillune/sdk
bash
yarn add @maillune/sdk
bash
pnpm add @maillune/sdk
html
<script src="https://cdn.jsdelivr.net/npm/@maillune/sdk/dist/email-builder.umd.js"></script>

3. Exchange for a session token (your server)

Before rendering the editor, your backend exchanges the secret API key for a short-lived session token scoped to the current user. The token lives for 2 hours and is safe to use in the browser.

js
// Node / Express — called once per page load, per user
app.get('/api/editor-session', requireAuth, async (req, res) => {
  const { sessionToken } = await fetch('https://api.maillune.com/v1/sessions', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.MAILLUNE_SECRET_KEY}`, // server-only env var
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ userId: req.user.id }),
  }).then(r => r.json());

  res.json({ sessionToken });
});

4. Embed the builder (your frontend)

Pass the session token — not your secret key — to the api-key attribute:

html
<!doctype html>
<html>
  <head>
    <script type="module">
      import '@maillune/sdk';

      // 1. Fetch the token from your own backend
      const { sessionToken } = await fetch('/api/editor-session').then(r => r.json());

      // 2. Set it on the element
      document.getElementById('builder').setAttribute('api-key', sessionToken);
    </script>
  </head>
  <body>
    <div style="height: 700px;">
      <email-builder id="builder" user-id="user_123"></email-builder>
    </div>
  </body>
</html>
jsx
'use client';
import { useEffect, useState } from 'react';
import '@maillune/sdk';

export default function EmailEditor({ userId }) {
  const [sessionToken, setSessionToken] = useState('');

  useEffect(() => {
    fetch('/api/editor-session')
      .then(r => r.json())
      .then(({ sessionToken }) => setSessionToken(sessionToken));
  }, []);

  if (!sessionToken) return <div>Loading…</div>;

  return (
    <div style={{ height: 700 }}>
      <email-builder api-key={sessionToken} user-id={userId} />
    </div>
  );
}
vue
<script setup>
import { ref, onMounted } from 'vue';
import '@maillune/sdk';

const props = defineProps({ userId: String });
const sessionToken = ref('');

onMounted(async () => {
  const { sessionToken: token } = await fetch('/api/editor-session').then(r => r.json());
  sessionToken.value = token;
});
</script>

<template>
  <div style="height: 700px">
    <email-builder
      v-if="sessionToken"
      :api-key="sessionToken"
      :user-id="userId"
    />
  </div>
</template>

INFO

The component requires a fixed height. It will fill its container — use height: 100vh or a specific pixel value. Minimum recommended height is 640px.

5. Handle save

Listen for the eb:save event to receive the user's design as a BlockTree JSON object. Store it in your database and pass it back when the user re-opens the editor.

js
const builder = document.querySelector('email-builder');

// Called when the user clicks Save
builder.addEventListener('eb:save', async (e) => {
  const { json } = e.detail;
  await fetch('/api/save-template', {
    method: 'POST',
    body: JSON.stringify(json),
  });
});

// Load a previously saved design
builder.load(savedBlockTree);

6. Export HTML (Pro+)

js
builder.addEventListener('eb:export', (e) => {
  const { html } = e.detail;
  // Send to your email service provider (SendGrid, Postmark, etc.)
  await sendEmail({ html, to: 'user@example.com' });
});

What's next?