API reference · Template builder & AI
Render a builder document
https://api.pidgely.com/template-builder/renderScale plan. Renders a builder document to the exact HTML and plain text a send would use (variable tags such as {{{FIRST_NAME|there}}} are kept for send time) and lints it for deliverability: all caps, spam phrases, link shorteners, image-only content, placeholder links, missing unsubscribe link. Nothing is stored.
Returns the exact HTML and text a send would use (variable tags kept) and the deliverability lint. Scale plan (403 plan_required otherwise).
Authorization: Bearer pdg_…. An API key with full_access. A sending_access key gets 401 restricted_api_key.
Body parameters
JSON object.
| Name | Type | Required | Description |
|---|---|---|---|
document |
object | Yes | Visual builder document (Pidgely extension, Scale plan). The server renders it to table-based e-mail HTML with Outlook (VML) buttons, responsive and dark-mode rules, and a plain-text version. |
document.version |
integer | No | One of 1. |
document.preview_text |
string | No | Preview text shown by mail clients next to the subject (inserted as hidden text at the top of the HTML). |
document.theme |
object | No | Global look of a builder e-mail. Colors are hex (#rrggbb). |
document.theme.brand_color |
string | No | — |
document.theme.text_color |
string | No | — |
document.theme.background_color |
string | No | — |
document.theme.content_color |
string | No | Background of the content card. |
document.theme.link_color |
string | No | — |
document.theme.font |
string | No | One of system, inter, helvetica, georgia, verdana, trebuchet. |
document.theme.content_width |
integer | No | Between 480 and 800. |
document.theme.radius |
integer | No | Between 0 and 32. |
document.theme.logo_url |
string | No | — |
document.theme.logo_width |
integer | No | — |
document.theme.logo_alt |
string | No | — |
document.theme.logo_align |
string | No | One of left, center, right. |
document.theme.logo_dark_url |
string | No | Logo variant shown in dark mode by clients that support it (Apple Mail, iOS); others keep logo_url. |
document.theme.button_text_color |
string | No | Label color of buttons on the brand color; empty picks white or near-black automatically. |
document.theme.web_font |
string | No | Google Fonts family loaded where e-mail clients support web fonts; font is the fallback stack. |
document.theme.heading_font |
string | No | Heading stack (empty = font). One of system, inter, helvetica, georgia, verdana, trebuchet. |
document.theme.heading_web_font |
string | No | Google Fonts family for headings (empty = web_font). |
document.blocks |
object[] | Yes | — |
document.blocks[].id |
string | No | Unique id (UUID). |
document.blocks[].type |
string | Yes | One of heading, text, button, image, divider, spacer, columns, list, quote, code, social, footer. |
document.blocks[].style |
object | No | — |
document.blocks[].style.align |
string | No | One of left, center, right. |
document.blocks[].style.color |
string | No | — |
document.blocks[].style.background |
string | No | — |
document.blocks[].style.font_size |
integer | No | Between 10 and 64. |
document.blocks[].style.padding |
object | No | — |
document.blocks[].level |
integer | No | One of 1, 2, 3. |
document.blocks[].text |
string | No | — |
document.blocks[].runs |
object[] | No | — |
document.blocks[].runs[].text |
string | Yes | — |
document.blocks[].runs[].bold |
boolean | No | — |
document.blocks[].runs[].italic |
boolean | No | — |
document.blocks[].runs[].href |
string | No | — |
document.blocks[].label |
string | No | — |
document.blocks[].href |
string | No | — |
document.blocks[].full_width |
boolean | No | — |
document.blocks[].src |
string | No | — |
document.blocks[].alt |
string | No | — |
document.blocks[].width |
integer | No | — |
document.blocks[].height |
integer | No | — |
document.blocks[].thickness |
integer | No | — |
document.blocks[].columns |
object[] | No | — |
document.blocks[].columns[].blocks |
object[] | Yes | — |
document.blocks[].ordered |
boolean | No | — |
document.blocks[].items |
object[][] | No | — |
document.blocks[].links |
object[] | No | — |
document.blocks[].links[].network |
string | Yes | One of x, linkedin, instagram, facebook, youtube, github, tiktok, discord, website. |
document.blocks[].links[].url |
string | Yes | Public https URL that receives the events. |
document.blocks[].unsubscribe |
boolean | No | — |
document.blocks[].unsubscribe_label |
string | No | — |
document.blocks[].unsubscribe_url |
string | No | Default {{{PIDGELY_UNSUBSCRIBE_URL}}} (filled per recipient by broadcasts; pass it as a variable on API sends). |
subject |
string | No | Subject line, up to 998 bytes. Required unless a template provides it. |
Request
This endpoint is in the Node.js SDK; the other SDKs don't cover it yet, so call it over HTTP from them.
import { Pidgely } from 'pidgely';
const pidgely = new Pidgely(process.env.PIDGELY_API_KEY);
const { data, error } = await pidgely.templateBuilder.render({
document: {
blocks: [
{
type: 'heading',
text: 'Welcome, {{{FIRST_NAME|there}}}',
},
{
type: 'button',
label: 'Get started',
href: 'https://acme.com/start',
},
],
},
subject: 'Welcome to Acme',
});
console.log(data ?? error);curl -X POST 'https://api.pidgely.com/template-builder/render' \
-H "Authorization: Bearer $PIDGELY_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"document": {
"blocks": [
{
"type": "heading",
"text": "Welcome, {{{FIRST_NAME|there}}}"
},
{
"type": "button",
"label": "Get started",
"href": "https://acme.com/start"
}
]
},
"subject": "Welcome to Acme"
}'Response
200 OK
{
"object": "template_builder",
"html": "<p>Hello!</p>",
"text": "Hello!",
"variables": [
"variable"
],
"issues": [
{
"code": "code",
"severity": "error",
"block_id": "0199d8a2-6f3c-7c51-9e2a-3b1f0c6d4e21",
"field": "field",
"message": "The `to` field is required.",
"detail": "detail"
}
],
"document": {
"version": 1,
"preview_text": "preview_text",
"theme": {
"brand_color": "brand_color",
"text_color": "text_color",
"background_color": "background_color",
"content_color": "content_color",
"link_color": "link_color",
"font": "system",
"content_width": 480,
"radius": 0,
"logo_url": "logo_url",
"logo_width": 1,
"logo_alt": "logo_alt",
"logo_align": "left",
"logo_dark_url": "logo_dark_url",
"button_text_color": "button_text_color",
"web_font": "web_font",
"heading_font": "system",
"heading_web_font": "heading_web_font"
},
"blocks": [
{
"id": "0199d8a2-6f3c-7c51-9e2a-3b1f0c6d4e21",
"type": "heading",
"style": {
"align": "left",
"color": "color",
"background": "background",
"font_size": 10,
"padding": {
"top": 1,
"right": 1,
"bottom": 1,
"left": 1
}
},
"level": 1,
"text": "Hello!",
"runs": [
{
"text": "Hello!",
"bold": false,
"italic": false,
"href": "href"
}
],
"label": "label",
"href": "href",
"full_width": false,
"src": "src",
"alt": "alt",
"width": 1,
"height": 1,
"thickness": 1,
"columns": [
{
"blocks": [
null
]
}
],
"ordered": false,
"items": [
[
{
"text": null,
"bold": null,
"italic": null,
"href": null
}
]
],
"links": [
{
"network": "x",
"url": "https://acme.com/webhooks/pidgely"
}
],
"unsubscribe": false,
"unsubscribe_label": "unsubscribe_label",
"unsubscribe_url": "unsubscribe_url"
}
]
}
}Response fields
| Name | Type | Required | Description |
|---|---|---|---|
object |
string | Yes | Object type. One of template_render. |
html |
string | Yes | HTML body. html or text is required (unless you send with a template). |
text |
string | Yes | — |
variables |
string[] | Yes | Typed variables (up to 50). FIRST_NAME, LAST_NAME, EMAIL and UNSUBSCRIBE_URL are reserved. |
issues |
object[] | Yes | — |
issues[].code |
string | Yes | all_caps, spam_words, excessive_punctuation, url_shortener, placeholder_link, insecure_link, empty_link, missing_image, missing_alt, missing_unsubscribe, image_only, link_ratio, missing_preview_text, missing_subject, subject_all_caps, subject_spam_words, subject_punctuation, subject_long, too_large, empty_document. |
issues[].severity |
string | Yes | One of error, warning, info. |
issues[].block_id |
string | No | — |
issues[].field |
string | No | — |
issues[].message |
string | Yes | — |
issues[].detail |
string | No | — |
document |
object | Yes | Visual builder document (Pidgely extension, Scale plan). The server renders it to table-based e-mail HTML with Outlook (VML) buttons, responsive and dark-mode rules, and a plain-text version. |
document.version |
integer | No | One of 1. |
document.preview_text |
string | No | Preview text shown by mail clients next to the subject (inserted as hidden text at the top of the HTML). |
document.theme |
object | No | Global look of a builder e-mail. Colors are hex (#rrggbb). |
document.theme.brand_color |
string | No | — |
document.theme.text_color |
string | No | — |
document.theme.background_color |
string | No | — |
document.theme.content_color |
string | No | Background of the content card. |
document.theme.link_color |
string | No | — |
document.theme.font |
string | No | One of system, inter, helvetica, georgia, verdana, trebuchet. |
document.theme.content_width |
integer | No | Between 480 and 800. |
document.theme.radius |
integer | No | Between 0 and 32. |
document.theme.logo_url |
string | No | — |
document.theme.logo_width |
integer | No | — |
document.theme.logo_alt |
string | No | — |
document.theme.logo_align |
string | No | One of left, center, right. |
document.theme.logo_dark_url |
string | No | Logo variant shown in dark mode by clients that support it (Apple Mail, iOS); others keep logo_url. |
document.theme.button_text_color |
string | No | Label color of buttons on the brand color; empty picks white or near-black automatically. |
document.theme.web_font |
string | No | Google Fonts family loaded where e-mail clients support web fonts; font is the fallback stack. |
document.theme.heading_font |
string | No | Heading stack (empty = font). One of system, inter, helvetica, georgia, verdana, trebuchet. |
document.theme.heading_web_font |
string | No | Google Fonts family for headings (empty = web_font). |
document.blocks |
object[] | Yes | — |
document.blocks[].id |
string | No | Unique id (UUID). |
document.blocks[].type |
string | Yes | One of heading, text, button, image, divider, spacer, columns, list, quote, code, social, footer. |
document.blocks[].style |
object | No | — |
document.blocks[].style.align |
string | No | One of left, center, right. |
document.blocks[].style.color |
string | No | — |
document.blocks[].style.background |
string | No | — |
document.blocks[].style.font_size |
integer | No | Between 10 and 64. |
document.blocks[].style.padding |
object | No | — |
document.blocks[].level |
integer | No | One of 1, 2, 3. |
document.blocks[].text |
string | No | — |
document.blocks[].runs |
object[] | No | — |
document.blocks[].runs[].text |
string | Yes | — |
document.blocks[].runs[].bold |
boolean | No | — |
document.blocks[].runs[].italic |
boolean | No | — |
document.blocks[].runs[].href |
string | No | — |
document.blocks[].label |
string | No | — |
document.blocks[].href |
string | No | — |
document.blocks[].full_width |
boolean | No | — |
document.blocks[].src |
string | No | — |
document.blocks[].alt |
string | No | — |
document.blocks[].width |
integer | No | — |
document.blocks[].height |
integer | No | — |
document.blocks[].thickness |
integer | No | — |
document.blocks[].columns |
object[] | No | — |
document.blocks[].columns[].blocks |
object[] | Yes | — |
document.blocks[].ordered |
boolean | No | — |
document.blocks[].items |
object[][] | No | — |
document.blocks[].links |
object[] | No | — |
document.blocks[].links[].network |
string | Yes | One of x, linkedin, instagram, facebook, youtube, github, tiktok, discord, website. |
document.blocks[].links[].url |
string | Yes | Public https URL that receives the events. |
document.blocks[].unsubscribe |
boolean | No | — |
document.blocks[].unsubscribe_label |
string | No | — |
document.blocks[].unsubscribe_url |
string | No | Default {{{PIDGELY_UNSUBSCRIBE_URL}}} (filled per recipient by broadcasts; pass it as a variable on API sends). |
Errors
Errors have the shape {"statusCode", "name", "message"}. See Errors for the full list.
| Status | Name | When |
|---|---|---|
| 401 | missing_api_key, restricted_api_key |
No Authorization: Bearer header, or a sending_access key called an endpoint that needs full_access. |
| 403 | invalid_api_key, validation_error |
The API key is invalid or revoked, or the request is not allowed for this account (unverified domain, plan limit, paused account). |
| 422 | validation_error |
A field is missing or invalid; message names it. |
| 429 | rate_limit_exceeded |
Too many requests per second. Wait for Retry-After seconds. |