API Documentation

Base URL: https://mailforge.grabshot.dev

All API endpoints require an API key passed via the x-api-key header. Get your free key from the dashboard.

Authentication

Include your API key in the x-api-key header with every request:

curl -H "x-api-key: mf_your_key_here" https://mailforge.grabshot.dev/v1/render

POST /v1/render

Render an HTML template with JSON data using Handlebars syntax. Supports variables, loops, conditionals, and helpers.

Parameters

FieldTypeDescription
templatestringHTML template with Handlebars syntax required
htmlstringAlias for template optional
dataobjectJSON data to inject into template optional
formatstringhtml (default) or inlined (also inline CSS) optional

Example: curl

curl -X POST https://mailforge.grabshot.dev/v1/render \
  -H "x-api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template": "<h1>Hello {{name}}</h1><p>Welcome to {{company}}</p>",
    "data": {"name": "John", "company": "Acme Inc"}
  }'

Example: Node.js

const resp = await fetch('https://mailforge.grabshot.dev/v1/render', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_KEY'
  },
  body: JSON.stringify({
    template: '<h1>Hello {{name}}</h1>',
    data: { name: 'John' }
  })
});
const { html } = await resp.json();

Example: Python

import requests

resp = requests.post(
    'https://mailforge.grabshot.dev/v1/render',
    headers={'x-api-key': 'YOUR_KEY'},
    json={
        'template': '<h1>Hello {{name}}</h1>',
        'data': {'name': 'John'}
    }
)
html = resp.json()['html']

Response

{
  "success": true,
  "html": "<h1>Hello John</h1><p>Welcome to Acme Inc</p>",
  "original_size": 52,
  "rendered_size": 48,
  "data_keys": 2
}

POST /v1/inline

Convert CSS <style> blocks to inline styles. Critical for email client compatibility - most email clients strip <style> tags.

Parameters

FieldTypeDescription
htmlstringHTML with CSS to inline required

Example

curl -X POST https://mailforge.grabshot.dev/v1/inline \
  -H "x-api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<style>h1{color:red;font-size:24px}</style><h1>Hello</h1>"}'

Response

{
  "success": true,
  "html": "<h1 style=\"color: red; font-size: 24px;\">Hello</h1>",
  "original_size": 54,
  "inlined_size": 52
}

POST /v1/preview

Render HTML email as a pixel-perfect screenshot. Useful for previewing how emails will look at different viewport widths.

Parameters

FieldTypeDescription
htmlstringHTML to render required*
templatestringHandlebars template (with data) optional
dataobjectTemplate data optional
widthnumberViewport width in px (default: 600) optional
heightnumberViewport height in px (default: 800) optional
formatstringpng (default) or jpeg optional
qualitynumberJPEG quality 1-100 (default: 80) optional

Example

curl -X POST https://mailforge.grabshot.dev/v1/preview \
  -H "x-api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1 style=\"color:orange\">Hello!</h1>", "width": 600}' \
  --output preview.png

Response

Returns the screenshot as a binary image (PNG or JPEG) with appropriate Content-Type header.

POST /v1/validate

Check HTML email for common compatibility issues, spam triggers, and best practices.

Parameters

FieldTypeDescription
htmlstringHTML email to validate required

Checks performed

Example

curl -X POST https://mailforge.grabshot.dev/v1/validate \
  -H "x-api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<html><body><img src=\"logo.png\"><script>alert(1)</script></body></html>"}'

Response

{
  "success": true,
  "score": 40,
  "issues": [
    {"type": "error", "code": "MISSING_ALT", "message": "1 image(s) missing alt attribute"},
    {"type": "error", "code": "JAVASCRIPT", "message": "JavaScript found. Email clients strip all JavaScript."}
  ],
  "warnings": [
    {"type": "warning", "code": "NO_DARK_MODE", "message": "No dark mode support detected."}
  ],
  "passes": [
    {"code": "INLINE_STYLES", "message": "No <style> blocks (good)"},
    {"code": "HTML_SIZE", "message": "HTML size is 0KB (under 102KB limit)"}
  ],
  "summary": {"errors": 2, "warnings": 1, "passes": 2, "images": 1, "size_kb": 0}
}

Other Endpoints

GET /v1/health

Health check. No auth required.

POST /v1/register

Create a new account. Body: {"email": "[email protected]"}

POST /v1/login

Validate an API key. Body: {"key": "mf_..."}

GET /v1/usage?api_key=YOUR_KEY

Get current usage stats and plan info.

GET /v1/billing/plans

List available plans and pricing.

POST /v1/billing/checkout

Create a Stripe checkout session for plan upgrade. Body: {"plan": "pro"}

Rate Limits

PlanRequests/minRequests/month
Free5100
Starter20500
Pro605,000
Business12050,000

Error Responses

All errors return JSON with success: false:

{
  "success": false,
  "error": "API key required. Pass via x-api-key header."
}
StatusMeaning
400Bad request (missing/invalid parameters)
401Unauthorized (missing or invalid API key)
429Rate limit or monthly limit exceeded
500Server error