Emails
Update Email Blocks
Replace an email body or mutate an existing block type.
PATCH
/
api
/
v1
/
emails
/
{emailId}
/
blocks
Update Email Blocks
curl --request PATCH \
--url https://api.sequenzy.com/api/v1/emails/{emailId}/blocks \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"html": "<string>",
"blocks": [
{}
],
"blockId": "<string>",
"type": "<string>",
"content": "<string>"
}
'import requests
url = "https://api.sequenzy.com/api/v1/emails/{emailId}/blocks"
payload = {
"html": "<string>",
"blocks": [{}],
"blockId": "<string>",
"type": "<string>",
"content": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
html: '<string>',
blocks: [{}],
blockId: '<string>',
type: '<string>',
content: '<string>'
})
};
fetch('https://api.sequenzy.com/api/v1/emails/{emailId}/blocks', 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://api.sequenzy.com/api/v1/emails/{emailId}/blocks",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'html' => '<string>',
'blocks' => [
[
]
],
'blockId' => '<string>',
'type' => '<string>',
'content' => '<string>'
]),
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://api.sequenzy.com/api/v1/emails/{emailId}/blocks"
payload := strings.NewReader("{\n \"html\": \"<string>\",\n \"blocks\": [\n {}\n ],\n \"blockId\": \"<string>\",\n \"type\": \"<string>\",\n \"content\": \"<string>\"\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://api.sequenzy.com/api/v1/emails/{emailId}/blocks")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"html\": \"<string>\",\n \"blocks\": [\n {}\n ],\n \"blockId\": \"<string>\",\n \"type\": \"<string>\",\n \"content\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.sequenzy.com/api/v1/emails/{emailId}/blocks")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"html\": \"<string>\",\n \"blocks\": [\n {}\n ],\n \"blockId\": \"<string>\",\n \"type\": \"<string>\",\n \"content\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"email": {
"id": "email_123",
"companyId": "company_123",
"name": "Welcome",
"subject": "Welcome",
"previewText": null,
"blocks": [{ "id": "legacy-block", "type": "html", "content": "<table>...</table>" }],
"createdAt": "2026-04-19T12:00:00.000Z",
"updatedAt": "2026-04-19T12:05:00.000Z"
}
}
{
"success": true,
"email": {
"id": "email_123",
"companyId": "company_123",
"name": "Welcome",
"subject": "Welcome",
"previewText": null,
"blocks": [
{
"id": "cta",
"type": "button",
"text": "Buy now",
"url": "https://example.com",
"variant": "primary"
}
],
"createdAt": "2026-04-19T12:00:00.000Z",
"updatedAt": "2026-04-19T12:05:00.000Z"
},
"warnings": [
"blocks[0].styles.color is not a supported field and was ignored. Button label color comes from the block-level `buttonTextColor` field. Supported styles fields: backgroundColor, backgroundOpacity, bleed, borderColor, borderRadius, borderWidth, paddingBottom, paddingLeft, paddingRight, paddingTop, textAlign, textColor."
]
}
{
"success": false,
"error": "Provide blockId to mutate a block or content to replace the body."
}
{
"success": false,
"error": "Missing API key. Provide via x-api-key header or Authorization: Bearer <key>"
}
{ "success": false, "error": "No company selected" }
{ "success": false, "error": "Block not found" }
{ "success": false, "error": "Internal server error" }
Update Email Blocks
You can replace an email body with raw HTML or structured blocks. You can also mutate an existing block betweentext and html.
Request
string
required
Email ID.
string
Raw HTML body. Replaces all blocks with a native HTML body.
array
Structured email blocks. Replaces all existing blocks. Put block styling under
styles; top-level style keys like backgroundColor, backgroundOpacity,
textColor, textAlign, borderColor, borderWidth, and borderRadius are
normalized into styles.Fields that are not part of a block’s schema are discarded rather than
rejected, so the request still succeeds. When that happens the response
includes a
warnings array naming each ignored field and listing the fields
that block does accept. Check warnings before treating a 200 as “every field
I sent took effect”. See Block styling vs block
fields.string
Existing block ID to mutate.
string
New block type. Type mutation supports
text and html.string
Optional replacement content for the mutated block.
curl -X PATCH "https://api.sequenzy.com/api/v1/emails/email_123/blocks" \
-H "Authorization: Bearer API_KEY" \
-H "Content-Type: application/json" \
-d '{"blockId":"legacy-block","type":"html"}'
Custom video thumbnails
SetthumbnailUrl on a YouTube video block to override YouTube’s still. This
endpoint replaces the blocks array, so omit thumbnailUrl in the replacement
block to clear the custom cover. The click destination always remains
videoUrl.
curl -X PATCH "https://api.sequenzy.com/api/v1/emails/email_123/blocks" \
-H "Authorization: Bearer API_KEY" \
-H "Content-Type: application/json" \
-d '{"blocks":[{"type":"video","videoUrl":"https://www.youtube.com/watch?v=dQw4w9WgXcQ","thumbnailUrl":"https://cdn.example.com/product-tour.jpg","alt":"Watch the product tour"}]}'
Block styling vs block fields
styles holds the layout and background of the block’s own container - the band
the block sits in. Some blocks render an inner element whose appearance is
controlled by dedicated top-level fields instead. Setting the styles key
changes the band, not the inner element.
The button block is the common case:
| What you want to change | Field to set | What styles does instead |
|---|---|---|
| Button fill color | buttonColor | styles.backgroundColor colors the band behind it |
| Button label color | buttonTextColor | styles.textColor does not reach the label |
buttonColor and buttonTextColor unset to inherit the email theme’s
primary color.
Body copy color is styles.textColor (or the top-level textColor alias). An
inline style on the <p> or <span> inside a text block’s content is not
the field the editor and theme use.
curl -X PATCH "https://api.sequenzy.com/api/v1/emails/email_123/blocks" \
-H "Authorization: Bearer API_KEY" \
-H "Content-Type: application/json" \
-d '{
"blocks": [
{
"id": "cta",
"type": "button",
"text": "Buy now",
"url": "https://example.com",
"variant": "primary",
"buttonColor": "#1d4ed8",
"buttonTextColor": "#ffffff"
}
]
}'
Responses
{
"success": true,
"email": {
"id": "email_123",
"companyId": "company_123",
"name": "Welcome",
"subject": "Welcome",
"previewText": null,
"blocks": [{ "id": "legacy-block", "type": "html", "content": "<table>...</table>" }],
"createdAt": "2026-04-19T12:00:00.000Z",
"updatedAt": "2026-04-19T12:05:00.000Z"
}
}
{
"success": true,
"email": {
"id": "email_123",
"companyId": "company_123",
"name": "Welcome",
"subject": "Welcome",
"previewText": null,
"blocks": [
{
"id": "cta",
"type": "button",
"text": "Buy now",
"url": "https://example.com",
"variant": "primary"
}
],
"createdAt": "2026-04-19T12:00:00.000Z",
"updatedAt": "2026-04-19T12:05:00.000Z"
},
"warnings": [
"blocks[0].styles.color is not a supported field and was ignored. Button label color comes from the block-level `buttonTextColor` field. Supported styles fields: backgroundColor, backgroundOpacity, bleed, borderColor, borderRadius, borderWidth, paddingBottom, paddingLeft, paddingRight, paddingTop, textAlign, textColor."
]
}
{
"success": false,
"error": "Provide blockId to mutate a block or content to replace the body."
}
{
"success": false,
"error": "Missing API key. Provide via x-api-key header or Authorization: Bearer <key>"
}
{ "success": false, "error": "No company selected" }
{ "success": false, "error": "Block not found" }
{ "success": false, "error": "Internal server error" }
⌘I
Update Email Blocks
curl --request PATCH \
--url https://api.sequenzy.com/api/v1/emails/{emailId}/blocks \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"html": "<string>",
"blocks": [
{}
],
"blockId": "<string>",
"type": "<string>",
"content": "<string>"
}
'import requests
url = "https://api.sequenzy.com/api/v1/emails/{emailId}/blocks"
payload = {
"html": "<string>",
"blocks": [{}],
"blockId": "<string>",
"type": "<string>",
"content": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
html: '<string>',
blocks: [{}],
blockId: '<string>',
type: '<string>',
content: '<string>'
})
};
fetch('https://api.sequenzy.com/api/v1/emails/{emailId}/blocks', 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://api.sequenzy.com/api/v1/emails/{emailId}/blocks",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'html' => '<string>',
'blocks' => [
[
]
],
'blockId' => '<string>',
'type' => '<string>',
'content' => '<string>'
]),
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://api.sequenzy.com/api/v1/emails/{emailId}/blocks"
payload := strings.NewReader("{\n \"html\": \"<string>\",\n \"blocks\": [\n {}\n ],\n \"blockId\": \"<string>\",\n \"type\": \"<string>\",\n \"content\": \"<string>\"\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://api.sequenzy.com/api/v1/emails/{emailId}/blocks")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"html\": \"<string>\",\n \"blocks\": [\n {}\n ],\n \"blockId\": \"<string>\",\n \"type\": \"<string>\",\n \"content\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.sequenzy.com/api/v1/emails/{emailId}/blocks")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"html\": \"<string>\",\n \"blocks\": [\n {}\n ],\n \"blockId\": \"<string>\",\n \"type\": \"<string>\",\n \"content\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"email": {
"id": "email_123",
"companyId": "company_123",
"name": "Welcome",
"subject": "Welcome",
"previewText": null,
"blocks": [{ "id": "legacy-block", "type": "html", "content": "<table>...</table>" }],
"createdAt": "2026-04-19T12:00:00.000Z",
"updatedAt": "2026-04-19T12:05:00.000Z"
}
}
{
"success": true,
"email": {
"id": "email_123",
"companyId": "company_123",
"name": "Welcome",
"subject": "Welcome",
"previewText": null,
"blocks": [
{
"id": "cta",
"type": "button",
"text": "Buy now",
"url": "https://example.com",
"variant": "primary"
}
],
"createdAt": "2026-04-19T12:00:00.000Z",
"updatedAt": "2026-04-19T12:05:00.000Z"
},
"warnings": [
"blocks[0].styles.color is not a supported field and was ignored. Button label color comes from the block-level `buttonTextColor` field. Supported styles fields: backgroundColor, backgroundOpacity, bleed, borderColor, borderRadius, borderWidth, paddingBottom, paddingLeft, paddingRight, paddingTop, textAlign, textColor."
]
}
{
"success": false,
"error": "Provide blockId to mutate a block or content to replace the body."
}
{
"success": false,
"error": "Missing API key. Provide via x-api-key header or Authorization: Bearer <key>"
}
{ "success": false, "error": "No company selected" }
{ "success": false, "error": "Block not found" }
{ "success": false, "error": "Internal server error" }