DocumentationSDK on GitHub

Documentation Guides

Render saved JSON to HTML

Generate personalized HTML and plain text from your backend without mounting an editor.

On this page

1. Load the saved document

Load the complete EmailDocument saved through onDocumentChange. The renderer accepts schema versions 1 and 2. Send the JSON itself; a template or draft ID cannot replace it.

2. Request HTML

POST to https://api.weavour.com/editors/:editorKey/render with document and optional mergeValues and conditionValues. Server requests need no dashboard session, user IDs or Origin header. Browser calls require an allowed origin.

typescript
// Run on your backend. email.json is the complete saved EmailDocument.
import { readFile } from 'node:fs/promises';
import type { EmailDocument } from '@weavour/editor';

async function renderSavedEmail(
  document: EmailDocument,
  mergeValues: Record<string, string> = {},
  conditionValues: Record<string, boolean> = {},
) {
  const editorKey = 'YOUR_PUBLIC_EDITOR_KEY';
  const response = await fetch(
    `https://api.weavour.com/editors/${editorKey}/render`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ document, mergeValues, conditionValues }),
    },
  );
  const result = await response.json();
  if (!response.ok) {
    throw Object.assign(new Error(result.error ?? 'Email rendering failed'), {
      status: response.status,
      issues: result.issues,
      retryAfter: response.headers.get('Retry-After'),
    });
  }
  return result as {
    html: string;
    text: string;
    subject: string;
    preheader: string;
    rendererVersion: string;
  };
}

const savedEmail: EmailDocument = JSON.parse(await readFile('./email.json', 'utf8'));
const { html, text, subject, preheader } = await renderSavedEmail(
  savedEmail,
  { first_name: 'Alex' }, // Use the stable IDs of tags in your document.
);
// Submit these fields through your own email delivery provider.
console.log(html);

3. Supply recipient values

mergeValues maps tag IDs to strings: { first_name: "Alex" }. Use the ID, not {{first_name}}. Supply values for every tag used in visible content, subject and preheader. Sample values are not substituted.

Text values are HTML escaped. URL values must fill a complete supported link field and use HTTP, HTTPS, mailto or tel. Missing values return 422 with the tag ID and location.

4. Supply condition outcomes

For conditional documents, evaluate your rules in your backend. Pass booleans in conditionValues, keyed by condition ID. A false parent removes its whole layout; values used only inside it are unnecessary. Missing outcomes for visible rules return 422.

typescript
// Evaluate your application's rules before calling the renderer.
const output = await renderSavedEmail(
  savedEmail,
  { first_name: 'Alex' },
  { free_account: true }, // Condition ID -> boolean, never an expression.
);
// output.html and output.text contain only matching layouts.

5. Send the result

The response contains html, text, subject, preheader and rendererVersion. Submit the email fields through your own provider. Rendering does not save or send. Images and timers must already have public URLs.

Limits and errors

Layout background images and custom global CSS are unsupported. For layout backgrounds, open the email in the editor and use exportHTML(). Unsupported content returns an error.

Maximum request: 1,000,000 UTF-8 bytes. Documents: 2,000 rendered nodes, depth 30. Merge values: 1,000 strings, up to 8,192 characters each.

HTTP statusAction
400 / 415Fix the request JSON, value types or Content-Type.
401 / 403Check the editor key and, for browser calls, the allowed origin.
413Reduce request size or document complexity.
422Read error and issues; fix missing values, invalid documents or unsupported content.
429 / 503Retry with jittered backoff and respect Retry-After when supplied.
Need help? Contact us