MailSlurp logo

Choose an email source for device rendering

Documentation navigation
Search documentation

Render a delivered email, import HTML or MIME, or send from your ESP to a render address.

Choose a source based on what you want to verify. A delivered message includes changes made by the sending platform; a template import is useful earlier while editing the design.

Source Use it when Start here
Stored email Your application test already received the message in MailSlurp Render by email ID
HTML You are reviewing template markup before sending HTML import
EML / raw MIME You have a complete exported email or test fixture MIME imports
Render address Your ESP or mail client can send a test message Send to a render address

Preview an exported file

The file endpoint accepts HTML, EML or a ZIP containing exactly one HTML or EML file, subject to archive limits. Keep the full EML when headers, multipart content or inline attachments affect the result. Do not rename an arbitrary file to .eml and assume it becomes valid MIME.

POST /emails/device-previews/import/file

Create a device preview run from an HTML, EML, or ZIP file

Uploads an HTML or RFC822 EML file, or a ZIP archive containing exactly one HTML or EML file. ZIP uploads are bounded by entry count, expanded size, and compression ratio.

Request, parameters, and responses

Query parameters

NameTypeRequiredDescription
subjectstringNo
fromstringNo
tostringNo
sourceAliasstringNo
externalIdstringNo
providersstring[]NoLegacy web-provider keys to render.
allTargetsbooleanNoRender every currently configured target.
targetsstring[]NoDevice preview target keys to render.
emailAuditbooleanNo
domainMonitorbooleanNo
senderDomainstringNo
folderIdstring:uuidNo

Request body

multipart/form-data

Responses

StatusSchemaDescription
200CreateDevicePreviewRunResultOK
HTTP examples

HTTP

HTTP
POST /emails/device-previews/import/file HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Content-Type: multipart/form-data; boundary=mailslurp-example-8402fc74cac41d822872a218
Accept: */*

--mailslurp-example-8402fc74cac41d822872a218
Content-Disposition: form-data; name="file"; filename="email.eml"
Content-Type: application/octet-stream

<binary contents of email.eml>
--mailslurp-example-8402fc74cac41d822872a218--

cURL

Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.

cURL
(
# Replace email.eml with your input file(s).
set -eu
response_file=$(mktemp)
request_body=$(mktemp)
trap 'rm -f "$response_file" "$request_body"' EXIT
{
  printf '%b' '--mailslurp-example-8402fc74cac41d822872a218\0015\0012Content-Disposition: form-data; name="file"; filename="email.eml"\0015\0012Content-Type: application/octet-stream\0015\0012\0015\0012'
  cat 'email.eml'
  printf '%b' '\0015\0012'
  printf '%b' '--mailslurp-example-8402fc74cac41d822872a218--\0015\0012'
} > "$request_body"
status=$(curl --silent --show-error \
  --request POST \
  --url 'https://api.mailslurp.com/emails/device-previews/import/file' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'Content-Type: multipart/form-data; boundary=mailslurp-example-8402fc74cac41d822872a218' \
  --header 'Accept: */*' \
  --output "$response_file" \
  --write-out '%{http_code}' \
  --data-binary "@$request_body")
case "$status" in
  2??) mv "$response_file" 'response.bin' ;;
  *) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)

JavaScript (HTTP)

Requires Node.js 22+. Tested with Node.js 22.14.0.

JavaScript (HTTP)
// Place the input files at the paths below before running this example.
import { readFile, writeFile } from "node:fs/promises";

const body = Buffer.concat([
  Buffer.from("--mailslurp-example-8402fc74cac41d822872a218\r\nContent-Disposition: form-data; name=\"file\"; filename=\"email.eml\"\r\nContent-Type: application/octet-stream\r\n\r\n", "utf8"),
  await readFile("email.eml"),
  Buffer.from("\r\n", "utf8"),
  Buffer.from("--mailslurp-example-8402fc74cac41d822872a218--\r\n", "utf8")
]);

const response = await fetch("https://api.mailslurp.com/emails/device-previews/import/file", {
  method: "POST",
  headers: {
    "x-api-key": "YOUR_API_KEY",
    "Content-Type": "multipart/form-data; boundary=mailslurp-example-8402fc74cac41d822872a218",
    "Accept": "*/*"
  },
  body,
  redirect: "manual",
  signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);

Python (HTTP)

Requires Python 3.9+. Tested with Python 3.9.21.

Python (HTTP)
# Place the input files at the paths below before running this example.
import http.client
from pathlib import Path
from urllib.parse import urlsplit

url = urlsplit("https://api.mailslurp.com/emails/device-previews/import/file")
headers = {
    "x-api-key": "YOUR_API_KEY",
    "Content-Type": "multipart/form-data; boundary=mailslurp-example-8402fc74cac41d822872a218",
    "Accept": "*/*"
}
body = b"".join([
    "--mailslurp-example-8402fc74cac41d822872a218\r\nContent-Disposition: form-data; name=\"file\"; filename=\"email.eml\"\r\nContent-Type: application/octet-stream\r\n\r\n".encode("utf-8"),
    Path("email.eml").read_bytes(),
    "\r\n".encode("utf-8"),
    "--mailslurp-example-8402fc74cac41d822872a218--\r\n".encode("utf-8")
])
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
    path = (url.path or "/") + ("?" + url.query if url.query else "")
    connection.request("POST", path, body=body, headers=headers)
    response = connection.getresponse()
    response_bytes = response.read()
    if not 200 <= response.status < 300:
        raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
    Path("response.bin").write_bytes(response_bytes)
finally:
    connection.close()

For a JSON-based integration, use base64 MIME import; for a byte stream or multipart upload, use the corresponding endpoint documented in the API workflow. Choose one source path per render rather than creating duplicate runs for every import form.

Receive the campaign from your ESP

Create or select a render address with the intended target profile. Send the finished test campaign to it and wait for its submission and run. Use the ESP guide table for platform-specific steps.

A missing submission means the message has not reached the render workflow yet. A submission with an unfinished run means rendering is still in progress. Inspect these separately when troubleshooting.

GET /emails/device-previews/email-submissions/wait

Wait for received device preview email submissions

Request, parameters, and responses

Query parameters

NameTypeRequiredDescription
sincestring:date-timeYes
emailDomainIdstring:uuidNo
profileIdstring:uuidNo
localPartstringNo
limitinteger:int32No
timeoutMillisinteger:int64No
pollIntervalMillisinteger:int64No
recipientEmailAddressstringNo

Responses

StatusSchemaDescription
200DevicePreviewEmailSubmissionWaitResultOK
HTTP examples

HTTP

HTTP
GET /emails/device-previews/email-submissions/wait?since=2026-01-01T00%3A00%3A00Z HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: */*

cURL

Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.

cURL
(
set -eu
response_file=$(mktemp)
trap 'rm -f "$response_file"' EXIT
status=$(curl --silent --show-error \
  --request GET \
  --url 'https://api.mailslurp.com/emails/device-previews/email-submissions/wait?since=2026-01-01T00%3A00%3A00Z' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'Accept: */*' \
  --output "$response_file" \
  --write-out '%{http_code}')
case "$status" in
  2??) mv "$response_file" 'response.bin' ;;
  *) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)

JavaScript (HTTP)

Requires Node.js 22+. Tested with Node.js 22.14.0.

JavaScript (HTTP)
import { writeFile } from "node:fs/promises";

const response = await fetch("https://api.mailslurp.com/emails/device-previews/email-submissions/wait?since=2026-01-01T00%3A00%3A00Z", {
  method: "GET",
  headers: {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
  },
  redirect: "manual",
  signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);

Python (HTTP)

Requires Python 3.9+. Tested with Python 3.9.21.

Python (HTTP)
import http.client
from pathlib import Path
from urllib.parse import urlsplit

url = urlsplit("https://api.mailslurp.com/emails/device-previews/email-submissions/wait?since=2026-01-01T00%3A00%3A00Z")
headers = {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
}
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
    path = (url.path or "/") + ("?" + url.query if url.query else "")
    connection.request("GET", path, headers=headers)
    response = connection.getresponse()
    response_bytes = response.read()
    if not 200 <= response.status < 300:
        raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
    Path("response.bin").write_bytes(response_bytes)
finally:
    connection.close()
GET /emails/device-previews/email-runs/wait

Wait for device preview runs created from account email domains

Request, parameters, and responses

Query parameters

NameTypeRequiredDescription
sincestring:date-timeYes
emailDomainIdstring:uuidNo
profileIdstring:uuidNo
localPartstringNo
limitinteger:int32No
timeoutMillisinteger:int64No
pollIntervalMillisinteger:int64No
hydrateThumbnailbooleanNoHydrate each returned run with a nullable primary screenshot thumbnail URL.

Responses

StatusSchemaDescription
200DevicePreviewIngestRunWaitResultOK
HTTP examples

HTTP

HTTP
GET /emails/device-previews/email-runs/wait?since=2026-01-01T00%3A00%3A00Z HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: */*

cURL

Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.

cURL
(
set -eu
response_file=$(mktemp)
trap 'rm -f "$response_file"' EXIT
status=$(curl --silent --show-error \
  --request GET \
  --url 'https://api.mailslurp.com/emails/device-previews/email-runs/wait?since=2026-01-01T00%3A00%3A00Z' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'Accept: */*' \
  --output "$response_file" \
  --write-out '%{http_code}')
case "$status" in
  2??) mv "$response_file" 'response.bin' ;;
  *) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)

JavaScript (HTTP)

Requires Node.js 22+. Tested with Node.js 22.14.0.

JavaScript (HTTP)
import { writeFile } from "node:fs/promises";

const response = await fetch("https://api.mailslurp.com/emails/device-previews/email-runs/wait?since=2026-01-01T00%3A00%3A00Z", {
  method: "GET",
  headers: {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
  },
  redirect: "manual",
  signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);

Python (HTTP)

Requires Python 3.9+. Tested with Python 3.9.21.

Python (HTTP)
import http.client
from pathlib import Path
from urllib.parse import urlsplit

url = urlsplit("https://api.mailslurp.com/emails/device-previews/email-runs/wait?since=2026-01-01T00%3A00%3A00Z")
headers = {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
}
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
    path = (url.path or "/") + ("?" + url.query if url.query else "")
    connection.request("GET", path, headers=headers)
    response = connection.getresponse()
    response_bytes = response.read()
    if not 200 <= response.status < 300:
        raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
    Path("response.bin").write_bytes(response_bytes)
finally:
    connection.close()

Continue with targets and profiles or CI automation.