Choose an email source for device rendering
Documentation navigation
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.
/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
| Name | Type | Required | Description |
|---|---|---|---|
subject | string | No | |
from | string | No | |
to | string | No | |
sourceAlias | string | No | |
externalId | string | No | |
providers | string[] | No | Legacy web-provider keys to render. |
allTargets | boolean | No | Render every currently configured target. |
targets | string[] | No | Device preview target keys to render. |
emailAudit | boolean | No | |
domainMonitor | boolean | No | |
senderDomain | string | No | |
folderId | string:uuid | No |
Request body
Responses
| Status | Schema | Description |
|---|---|---|
200 | CreateDevicePreviewRunResult | OK |
HTTP examples
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.
(
# 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.
// 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.
# 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.
/emails/device-previews/email-submissions/wait
Wait for received device preview email submissions
Request, parameters, and responses
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
since | string:date-time | Yes | |
emailDomainId | string:uuid | No | |
profileId | string:uuid | No | |
localPart | string | No | |
limit | integer:int32 | No | |
timeoutMillis | integer:int64 | No | |
pollIntervalMillis | integer:int64 | No | |
recipientEmailAddress | string | No |
Responses
| Status | Schema | Description |
|---|---|---|
200 | DevicePreviewEmailSubmissionWaitResult | OK |
HTTP examples
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.
(
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.
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.
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()
/emails/device-previews/email-runs/wait
Wait for device preview runs created from account email domains
Request, parameters, and responses
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
since | string:date-time | Yes | |
emailDomainId | string:uuid | No | |
profileId | string:uuid | No | |
localPart | string | No | |
limit | integer:int32 | No | |
timeoutMillis | integer:int64 | No | |
pollIntervalMillis | integer:int64 | No | |
hydrateThumbnail | boolean | No | Hydrate each returned run with a nullable primary screenshot thumbnail URL. |
Responses
| Status | Schema | Description |
|---|---|---|
200 | DevicePreviewIngestRunWaitResult | OK |
HTTP examples
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.
(
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.
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.
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.
Running this code needs an API key. Get one free