curl --request POST \
--url https://brew.new/api/v1/emails/{emailId}/export \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"provider": "klaviyo",
"templateName": "Welcome — Brew export"
}
'import requests
url = "https://brew.new/api/v1/emails/{emailId}/export"
payload = {
"provider": "klaviyo",
"templateName": "Welcome — Brew export"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({provider: 'klaviyo', templateName: 'Welcome — Brew export'})
};
fetch('https://brew.new/api/v1/emails/{emailId}/export', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://brew.new/api/v1/emails/{emailId}/export",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'provider' => 'klaviyo',
'templateName' => 'Welcome — Brew export'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://brew.new/api/v1/emails/{emailId}/export"
payload := strings.NewReader("{\n \"provider\": \"klaviyo\",\n \"templateName\": \"Welcome — Brew export\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://brew.new/api/v1/emails/{emailId}/export")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"provider\": \"klaviyo\",\n \"templateName\": \"Welcome — Brew export\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://brew.new/api/v1/emails/{emailId}/export")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"provider\": \"klaviyo\",\n \"templateName\": \"Welcome — Brew export\"\n}"
response = http.request(request)
puts response.read_body{
"emailId": "eml_2SmZOWV3ZQ7W5x6g3m4p",
"provider": "klaviyo",
"providerName": "Klaviyo",
"templateName": "Welcome — Brew export",
"dryRun": true
}{
"emailId": "eml_2SmZOWV3ZQ7W5x6g3m4p",
"provider": "klaviyo",
"providerName": "Klaviyo",
"templateName": "Welcome — Brew export",
"templateId": "Ab3Kd9",
"dryRun": false
}{
"error": {
"code": "INTEGRATION_NOT_CONNECTED",
"type": "invalid_request",
"message": "Klaviyo isn't connected for this brand. Connect it on the Integrations page, then export.",
"suggestion": "Connect Klaviyo on the Integrations page, then retry the export.",
"docs": "https://docs.brew.new/api-reference/api/errors",
"param": "provider"
}
}{
"error": {
"code": "INVALID_API_KEY",
"type": "authentication_error",
"message": "The provided API key is invalid.",
"suggestion": "Check the API key format and retry with a valid active key.",
"docs": "https://docs.brew.new/api-reference/api/authentication"
}
}{
"error": {
"code": "INSUFFICIENT_PERMISSIONS",
"type": "authorization_error",
"message": "The caller does not have the required permission.",
"suggestion": "Use an API key or session with the required permission.",
"docs": "https://docs.brew.new/api-reference/api/authentication",
"param": "emails"
}
}{
"error": {
"code": "EMAIL_NOT_FOUND",
"type": "not_found",
"message": "No email exists with id 'eml_2SmZOWV3ZQ7W5x6g3m4p'.",
"suggestion": "List designs with GET /v1/emails to find a valid emailId.",
"docs": "https://docs.brew.new/api-reference/api/errors",
"param": "emailId"
}
}{
"error": {
"code": "IDEMPOTENCY_CONFLICT",
"type": "conflict",
"message": "The same idempotency key was reused with a different request payload.",
"suggestion": "Reuse the original payload or send a new idempotency key.",
"docs": "https://docs.brew.new/api-reference/api/idempotency"
}
}{
"error": {
"code": "RATE_LIMITED",
"type": "rate_limit",
"message": "Too many requests.",
"suggestion": "Wait for the retry window before sending another request.",
"docs": "https://docs.brew.new/api-reference/api/rate-limits",
"retryAfter": 42
}
}{
"error": {
"code": "INTERNAL_ERROR",
"type": "internal_error",
"message": "An unexpected error occurred.",
"suggestion": "Retry the request. If it keeps failing, contact support.",
"docs": "https://docs.brew.new/api-reference/api/errors"
}
}{
"error": {
"code": "EXPORT_PROVIDER_ERROR",
"type": "service_unavailable",
"message": "Klaviyo rejected the template export.",
"suggestion": "Check the connection in Klaviyo and retry. Reconnect it if authorization has expired.",
"docs": "https://docs.brew.new/api-reference/api/errors"
}
}Export a design to an ESP
Export a finished email DESIGN to a connected third-party ESP (Klaviyo, Mailchimp, HubSpot, Braze, Iterable, Postmark, OneSignal, SendGrid, Mailgun) as a reusable TEMPLATE. This is NOT a Brew send — it creates a template in the destination platform and does NOT deliver to recipients, and is unrelated to Brew sending domains or audiences. The provider must already be connected for the brand (connect ESPs in the Brew app). The exported HTML is automatically made ESP-portable. Pass dryRun: true (alias dry_run) to validate the design, brand ownership, and ESP connection without creating a template. Free (no credits).
curl --request POST \
--url https://brew.new/api/v1/emails/{emailId}/export \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"provider": "klaviyo",
"templateName": "Welcome — Brew export"
}
'import requests
url = "https://brew.new/api/v1/emails/{emailId}/export"
payload = {
"provider": "klaviyo",
"templateName": "Welcome — Brew export"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({provider: 'klaviyo', templateName: 'Welcome — Brew export'})
};
fetch('https://brew.new/api/v1/emails/{emailId}/export', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://brew.new/api/v1/emails/{emailId}/export",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'provider' => 'klaviyo',
'templateName' => 'Welcome — Brew export'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://brew.new/api/v1/emails/{emailId}/export"
payload := strings.NewReader("{\n \"provider\": \"klaviyo\",\n \"templateName\": \"Welcome — Brew export\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://brew.new/api/v1/emails/{emailId}/export")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"provider\": \"klaviyo\",\n \"templateName\": \"Welcome — Brew export\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://brew.new/api/v1/emails/{emailId}/export")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"provider\": \"klaviyo\",\n \"templateName\": \"Welcome — Brew export\"\n}"
response = http.request(request)
puts response.read_body{
"emailId": "eml_2SmZOWV3ZQ7W5x6g3m4p",
"provider": "klaviyo",
"providerName": "Klaviyo",
"templateName": "Welcome — Brew export",
"dryRun": true
}{
"emailId": "eml_2SmZOWV3ZQ7W5x6g3m4p",
"provider": "klaviyo",
"providerName": "Klaviyo",
"templateName": "Welcome — Brew export",
"templateId": "Ab3Kd9",
"dryRun": false
}{
"error": {
"code": "INTEGRATION_NOT_CONNECTED",
"type": "invalid_request",
"message": "Klaviyo isn't connected for this brand. Connect it on the Integrations page, then export.",
"suggestion": "Connect Klaviyo on the Integrations page, then retry the export.",
"docs": "https://docs.brew.new/api-reference/api/errors",
"param": "provider"
}
}{
"error": {
"code": "INVALID_API_KEY",
"type": "authentication_error",
"message": "The provided API key is invalid.",
"suggestion": "Check the API key format and retry with a valid active key.",
"docs": "https://docs.brew.new/api-reference/api/authentication"
}
}{
"error": {
"code": "INSUFFICIENT_PERMISSIONS",
"type": "authorization_error",
"message": "The caller does not have the required permission.",
"suggestion": "Use an API key or session with the required permission.",
"docs": "https://docs.brew.new/api-reference/api/authentication",
"param": "emails"
}
}{
"error": {
"code": "EMAIL_NOT_FOUND",
"type": "not_found",
"message": "No email exists with id 'eml_2SmZOWV3ZQ7W5x6g3m4p'.",
"suggestion": "List designs with GET /v1/emails to find a valid emailId.",
"docs": "https://docs.brew.new/api-reference/api/errors",
"param": "emailId"
}
}{
"error": {
"code": "IDEMPOTENCY_CONFLICT",
"type": "conflict",
"message": "The same idempotency key was reused with a different request payload.",
"suggestion": "Reuse the original payload or send a new idempotency key.",
"docs": "https://docs.brew.new/api-reference/api/idempotency"
}
}{
"error": {
"code": "RATE_LIMITED",
"type": "rate_limit",
"message": "Too many requests.",
"suggestion": "Wait for the retry window before sending another request.",
"docs": "https://docs.brew.new/api-reference/api/rate-limits",
"retryAfter": 42
}
}{
"error": {
"code": "INTERNAL_ERROR",
"type": "internal_error",
"message": "An unexpected error occurred.",
"suggestion": "Retry the request. If it keeps failing, contact support.",
"docs": "https://docs.brew.new/api-reference/api/errors"
}
}{
"error": {
"code": "EXPORT_PROVIDER_ERROR",
"type": "service_unavailable",
"message": "Klaviyo rejected the template export.",
"suggestion": "Check the connection in Klaviyo and retry. Reconnect it if authorization has expired.",
"docs": "https://docs.brew.new/api-reference/api/errors"
}
}Authorizations
Send your Brew API key as Authorization: Bearer brew_xxx.
Headers
Optional idempotency key for safe retries. Reusing the same key with the same request body returns the original response for 24 hours.
1 - 100The brand this request acts on. REQUIRED for organization-scoped credentials (otherwise 400 BRAND_ID_REQUIRED — there is no default brand); list ids with GET /v1/brands. Brand-scoped credentials may omit it, and sending a different brand returns 403 BRAND_SCOPE_MISMATCH. A brand outside your organization returns 404 BRAND_NOT_FOUND.
1 - 64Path Parameters
Design id returned by POST /v1/emails and listed by GET /v1/emails.
1 - 64"eml_2SmZOWV3ZQ7W5x6g3m4p"
Body
The connected ESP to export the design to as a template.
braze, hubspot, klaviyo, mailchimp, iterable, postmark, onesignal, mailgun, sendgrid Template name in the ESP. Defaults to the email title.
1 - 100Validate the design, brand ownership, and ESP connection without creating a template.
Accepted alias of dryRun.
Response
Dry run — the design + ESP connection were validated without creating a template.
Was this page helpful?