n8n Workflow
Expert

Automated Dartagnan to Braze Email Template Sync

Automate your email marketing workflow by synchronizing templates from Dartagnan to Braze. This n8n workflow automatically creates and updates Braze templates, including image URL processing.

Overview

Automatically synchronizes email templates from Dartagnan to Braze, creating new templates and updating existing ones based on modification dates, ensuring your marketing campaigns are always up-to-date.

🧠 Description & Use Case

This workflow automates the synchronization of email templates from Dartagnan, a specialized email builder, to Braze, a customer engagement platform. It runs on a schedule, identifies new or updated templates in Dartagnan, processes their HTML to ensure images are correctly embedded, and then creates or updates the corresponding templates in Braze.

🔄 How It Works:

  1. Scheduled Trigger & Authentication: The workflow is triggered every 5 minutes. It begins by loading necessary credentials and requesting a new access token from the Dartagnan API.

  2. Fetch Data from Both Platforms:

    • Dartagnan: It retrieves a list of all projects and their associated campaigns.
    • Braze: It fetches a complete list of existing email templates.
  3. Compare and Differentiate: The workflow merges the lists of Dartagnan campaigns and Braze templates. Using the Merge node's keepNonMatches option, it intelligently separates the campaigns into two paths:

    • Create Path: Campaigns that exist in Dartagnan but not in Braze.
    • Update Path: Campaigns that exist in both platforms.
  4. Update Existing Templates (Update Path):

    • An IF node checks if the Dartagnan campaign's update_date is more recent than the Braze template's updated_at timestamp.
    • If an update is needed, it fetches the full campaign content (HTML, text, and media URLs) from Dartagnan.
    • A custom Code Node (Embed image in HTML) processes the HTML, replacing all local image paths with their absolute, public URLs from the medias object. This is critical for images to render correctly in Braze.
    • The processed HTML and text bodies are encoded and sent to the Braze API to update the existing email template.
  5. Create New Templates (Create Path):

    • The workflow fetches the full campaign content for the new template from Dartagnan.
    • It uses the same Code Node logic to process the HTML and replace image paths with public URLs.
    • The prepared content is then sent to the Braze API to create a new email template.

✅ Real-World Use Cases:

  • Marketing Operations Efficiency: Automate the tedious, manual process of transferring email designs from a builder like Dartagnan to a marketing automation platform like Braze, saving time and reducing human error.
  • Seamless Design-to-Campaign Pipeline: Enable a smooth workflow where email designers can work and push updates in their preferred tool (Dartagnan), and those changes are automatically reflected in Braze, ready for campaign managers to use.
  • Version Control and Brand Consistency: Ensure that the templates used in Braze campaigns are always the latest, approved versions from the design source, preventing outdated or incorrect designs from being sent to customers.
  • Scalable Template Management: Effortlessly manage a large and growing library of email templates across both platforms, ensuring synchronization without needing to scale up manual effort.

Instructions

Basic steps

  1. Download workflow JSON
  2. Import into n8n
  3. Configure node parameters as needed
  4. Test and enable the workflow

💡 Tips

After importing, verify all connectors and credentials match your environment.

Preview

Workflow Visualization

28 nodes
100%
Token RequesthttpRequestAssign CredentialssetDartagnan Project listhttpRequestCreate email templatehttpRequestList Available Email Template BrazehttpRequestFiltered Project CampaignhttpRequestFiltering Dartagnan CampaignssetSplit OutsplitOutFiltering Braze Email TemplatesetNot existing In BrazemergeExisting In BrazemergeDartagnan HTML & MEDIA To UpdatehttpRequestEncode Content To UpdatesetDartagnan HTML & MEDIA Campagne to CreatehttpRequestEncode Content to CreatesetIf campaign is modified recentlyifFilter Braze vs DartagnanifEmbed image in HTMLcodeEmbed image in HTML 1codeSticky NotestickyNoteSticky Note1stickyNoteSticky Note2stickyNoteSticky Note3stickyNoteSticky Note4stickyNoteSticky Note5stickyNoteSticky Note6stickyNoteEvery 5 minutes startscheduleTriggerUpdate existing email template in BrazehttpRequest

Nodes

1

Token Request

httpRequest

v4.2
2

Assign Credentials

set

v3.4
3

Dartagnan Project list

httpRequest

v4.2
4

Create email template

httpRequest

v4.2
5

List Available Email Template Braze

httpRequest

v4.2
6

Filtered Project Campaign

httpRequest

v4.2
7

Filtering Dartagnan Campaigns

set

v3.4
8

Split Out

splitOut

v1
9

Filtering Braze Email Template

set

v3.4
10

Not existing In Braze

merge

v3
11

Existing In Braze

merge

v3
12

Dartagnan HTML & MEDIA To Update

httpRequest

v4.2
13

Encode Content To Update

set

v3.4
14

Dartagnan HTML & MEDIA Campagne to Create

httpRequest

v4.2
15

Encode Content to Create

set

v3.4
16

If campaign is modified recently

if

v2.2
17

Filter Braze vs Dartagnan

if

v2.2
18

Embed image in HTML

code

v2
19

Embed image in HTML 1

code

v2
20

Sticky Note

stickyNote

v1
21

Sticky Note1

stickyNote

v1
22

Sticky Note2

stickyNote

v1
23

Sticky Note3

stickyNote

v1
24

Sticky Note4

stickyNote

v1
25

Sticky Note5

stickyNote

v1
26

Sticky Note6

stickyNote

v1
27

Every 5 minutes start

scheduleTrigger

v1.2
28

Update existing email template in Braze

httpRequest

v4.2

Statistics

Total nodes:28
Disabled nodes:0
Node types:8
Connections:19
View full JSON structure
{
  "nodes": [
    {
      "id": "1562791c-33a9-425c-a774-32e328bd4715",
      "name": "Token Request",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Get the token from Dartagnan that expires after 60 minutes",
      "position": [
        60,
        200
      ],
      "parameters": {
        "url": "https://app.dartagnan.io/oauth/v2/token",
        "method": "POST",
        "options": {
          "redirect": {
            "redirect": {}
          }
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "client_id",
              "value": "={{ $('Assign Credentials').item.json.client_id }}"
            },
            {
              "name": "client_secret",
              "value": "={{ $('Assign Credentials').item.json.client_secret }}"
            },
            {
              "name": "grant_type",
              "value": "client_credentials"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "2d81394c-9898-419a-a832-339e66d56a29",
      "name": "Assign Credentials",
      "type": "n8n-nodes-base.set",
      "position": [
        -400,
        300
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "debe5309-40f6-411d-8c4d-3b282cf1bba9",
              "name": "client_id",
              "type": "string",
              "value": "Enter your Dartagnan client_id"
            },
            {
              "id": "6028c0c0-a701-449e-952e-46895280e4ef",
              "name": "client_secret",
              "type": "string",
              "value": "Enter your Dartagnan client_secret"
            },
            {
              "id": "7e82aa01-18ff-4b76-802b-cc8cae987614",
              "name": "instance_url",
              "type": "string",
              "value": "Enter your Braze instance_url like https://rest.fra-02.braze.eu for example"
            },
            {
              "id": "a3c641d7-fdbd-4e96-a845-e2c5aad93398",
              "name": "api_key",
              "type": "string",
              "value": "Enter your Braze API key"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "b80a8eda-bbf6-4560-8665-5128a97db217",
      "name": "Dartagnan Project list",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        360,
        200
      ],
      "parameters": {
        "url": "https://app.dartagnan.io/api/public/projects",
        "options": {
          "redirect": {
            "redirect": {}
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.access_token }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "909eb4f9-d67e-4f5e-bdb6-67ef2da813a4",
      "name": "Create email template",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2420,
        400
      ],
      "parameters": {
        "url": "=https://{{ $item(\"0\").$node[\"Assign Credentials\"].json[\"instance_url\"] }}/templates/email/create",
        "method": "POST",
        "options": {
          "redirect": {
            "redirect": {}
          }
        },
        "jsonBody": "={\n  \"template_name\": \"{{ $('Filter Braze vs Dartagnan').item.json.unified_name }}\",\n  \"subject\": \"Subject Line\",\n  \"body\": {{ $json.encoded_html }},\n  \"plaintext_body\":{{ $json.encoded_plaintext_body }}\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $item(\"0\").$node[\"Assign Credentials\"].json[\"api_key\"] }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "0820220a-5ea6-42eb-bfd2-f697dcf37a8a",
      "name": "List Available Email Template Braze",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        60,
        420
      ],
      "parameters": {
        "url": "=https://{{ $('Assign Credentials').item.json.instance_url }}/templates/email/list ",
        "options": {
          "redirect": {
            "redirect": {}
          }
        },
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {}
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Assign Credentials').item.json.api_key }}"
            }
          ]
        }
      },
      "executeOnce": false,
      "typeVersion": 4.2
    },
    {
      "id": "dcfec31d-c42c-4545-91a0-3b2e3350d375",
      "name": "Filtered Project Campaign",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "POC Avec la derniere valeur avant iteration sur une boucle for dans la v2",
      "position": [
        640,
        200
      ],
      "parameters": {
        "url": "=https://app.dartagnan.io/api/public/projects/{{ $json.id }}",
        "options": {
          "redirect": {
            "redirect": {}
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer  {{ $('Token Request').item.json.access_token }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "1aba6e4c-d3b0-49f8-8bcb-bece8cce4b4d",
      "name": "Filtering Dartagnan Campaigns",
      "type": "n8n-nodes-base.set",
      "position": [
        840,
        200
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "e82823f9-e094-48e6-9d10-8d10c126ffa8",
              "name": "id",
              "type": "string",
              "value": "={{ $json.campaigns[0].id }}"
            },
            {
              "id": "31ddce2b-7366-448c-a68f-bfea4dcb057b",
              "name": "campaign_name",
              "type": "string",
              "value": "={{ $json.campaigns[0].name }}"
            },
            {
              "id": "0eb93a91-fbd5-45f9-b47c-89340f716909",
              "name": "=unified_name",
              "type": "string",
              "value": "={{ $json.campaigns[0].name }}-{{ $json.campaigns[0].id }}"
            },
            {
              "id": "e784ad19-301f-45f7-b90b-27bcce06b6dc",
              "name": "creation_date",
              "type": "string",
              "value": "={{ $json.campaigns[0].created }}"
            },
            {
              "id": "8e99802a-1342-4482-afc7-fbe22df6cffc",
              "name": "update_date",
              "type": "string",
              "value": "={{ $json.campaigns[0].updated }}"
            },
            {
              "id": "f5814048-5cb6-465f-a95a-c56913c9ed2d",
              "name": "created_by",
              "type": "string",
              "value": "={{ $json.campaigns[0].createdBy.firstname }} {{ $json.campaigns[0].createdBy.lastname }}"
            },
            {
              "id": "19442a15-bf61-47e1-987f-709de70f8f08",
              "name": "modified_by",
              "type": "string",
              "value": "={{ $json.campaigns[0].updatedBy.firstname }} {{ $json.campaigns[0].updatedBy.lastname }}"
            },
            {
              "id": "b75db148-0006-4327-a418-49347da5b970",
              "name": "access_token",
              "type": "string",
              "value": "={{ $('Token Request').item.json.access_token }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "ee26ca98-2c42-4e69-ab34-cc2d737c22f8",
      "name": "Split Out",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        360,
        420
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "templates"
      },
      "typeVersion": 1
    },
    {
      "id": "08d8372f-38cb-4722-95aa-c89bfcc36dff",
      "name": "Filtering Braze Email Template",
      "type": "n8n-nodes-base.set",
      "position": [
        800,
        420
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "920fd95b-bf48-494d-9d55-d5d961fd459f",
              "name": "braze_template_name",
              "type": "string",
              "value": "={{ $json.template_name }}"
            },
            {
              "id": "8425c3c2-07c3-4c3d-a58e-e8d1460fea9c",
              "name": "email_template_id",
              "type": "string",
              "value": "={{ $json.email_template_id }}"
            },
            {
              "id": "1944b20a-147b-4b4d-9558-f2927de9b2f2",
              "name": "created_at",
              "type": "string",
              "value": "={{ $json.created_at }}"
            },
            {
              "id": "850a4a0a-d27b-43cb-a8a3-2d810a5fdc13",
              "name": "updated_at",
              "type": "string",
              "value": "={{ $json.updated_at }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "3e07fa37-8afd-4d88-85a3-53a4f000f898",
      "name": "Not existing In Braze",
      "type": "n8n-nodes-base.merge",
      "position": [
        1160,
        420
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "advanced": true,
        "joinMode": "keepNonMatches",
        "mergeByFields": {
          "values": [
            {
              "field1": "unified_name",
              "field2": "braze_template_name"
            }
          ]
        }
      },
      "typeVersion": 3
    },
    {
      "id": "210a9d44-f58f-4788-89bd-934dfc0fca41",
      "name": "Existing In Braze",
      "type": "n8n-nodes-base.merge",
      "position": [
        1140,
        200
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "advanced": true,
        "mergeByFields": {
          "values": [
            {
              "field1": "unified_name",
              "field2": "braze_template_name"
            }
          ]
        }
      },
      "typeVersion": 3
    },
    {
      "id": "59f8270b-b988-452b-aaf0-0e7b35490449",
      "name": "Dartagnan HTML & MEDIA To Update",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1740,
        180
      ],
      "parameters": {
        "url": "=https://app.dartagnan.io/api/public/campaigns/{{ $json.id }}",
        "options": {
          "redirect": {
            "redirect": {}
          }
        },
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {}
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer  {{ $('Token Request').item.json.access_token }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "04b26314-cb00-4a90-a7fe-019936a5ba2a",
      "name": "Encode Content To Update",
      "type": "n8n-nodes-base.set",
      "position": [
        2180,
        180
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "fb308822-842f-4559-b5c9-96853c79e00e",
              "name": "encoded_html",
              "type": "string",
              "value": "={{JSON.stringify( $json.html  )}} "
            },
            {
              "id": "390b747b-967d-45f4-9faf-3aaed605d769",
              "name": "encoded_plaintext_body",
              "type": "string",
              "value": "={{JSON.stringify($json.text)}} "
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "1455fd2e-85ee-4844-8c4b-755870dc9cee",
      "name": "Dartagnan HTML & MEDIA Campagne to Create",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1760,
        400
      ],
      "parameters": {
        "url": "=https://app.dartagnan.io/api/public/campaigns/{{ $json.id }}",
        "options": {
          "redirect": {
            "redirect": {}
          }
        },
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {}
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer  {{ $('Token Request').item.json.access_token }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "214d8b4b-c512-4d1f-8115-8491f6fc9272",
      "name": "Encode Content to Create",
      "type": "n8n-nodes-base.set",
      "position": [
        2200,
        400
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "fb308822-842f-4559-b5c9-96853c79e00e",
              "name": "encoded_html",
              "type": "string",
              "value": "={{JSON.stringify( $json.html  )}} "
            },
            {
              "id": "390b747b-967d-45f4-9faf-3aaed605d769",
              "name": "encoded_plaintext_body",
              "type": "string",
              "value": "={{JSON.stringify($json.text)}} "
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "90158667-eeda-4d44-b45c-c6e7fa02e664",
      "name": "If campaign is modified recently",
      "type": "n8n-nodes-base.if",
      "position": [
        1420,
        200
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "7e9c9fd3-2ef9-47ce-bccb-9b3320472d36",
              "operator": {
                "type": "dateTime",
                "operation": "after"
              },
              "leftValue": "={{ $json.update_date }}",
              "rightValue": "={{ $json.updated_at }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "55aecc9b-d5a6-4a6b-a986-3ddfae200b94",
      "name": "Filter Braze vs Dartagnan",
      "type": "n8n-nodes-base.if",
      "position": [
        1440,
        420
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "7e9c9fd3-2ef9-47ce-bccb-9b3320472d36",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.unified_name }}",
              "rightValue": "="
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ff2a47df-983f-42f5-9c96-431fccbdb8ef",
      "name": "Embed image in HTML",
      "type": "n8n-nodes-base.code",
      "position": [
        1960,
        180
      ],
      "parameters": {
        "jsCode": "// n8n Code Node: HTML Image URL Replacer\n// This script replaces all image references in HTML with their direct URLs\n// It's designed to work with the data structure you provided\n\n/**\n * Main function to process the incoming data in n8n\n * Expects an item with html and medias properties\n */\nfunction processData(item) {\n  // Extract the HTML and media mappings from the item\n  const { html, medias } = item.json;\n\n  // Process the HTML and replace all image references\n  const processedHtml = replaceImageReferences(html, medias);\n\n  // Return the processed data\n  return {\n    json: {\n      ...item.json,\n      html: processedHtml\n    }\n  };\n}\n\n/**\n * Replace all image references in HTML with direct URLs\n * @param {string} html - The HTML content to process\n * @param {Object} medias - Key-value pairs mapping image paths to direct URLs\n * @returns {string} - Processed HTML with direct URLs\n */\nfunction replaceImageReferences(html, medias) {\n  if (!html || !medias) {\n    throw new Error('Missing required parameters: html or medias');\n  }\n  \n  let updatedHtml = html;\n  \n  // Process each media URL\n  Object.entries(medias).forEach(([imagePath, directUrl]) => {\n    // For safety, escape special regex characters in the image path\n    const escapedPath = imagePath.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n    \n    // 1. Replace in img src attributes\n    updatedHtml = updatedHtml.replace(\n      new RegExp(`src=[\"']${escapedPath}[\"']`, 'g'), \n      match => match.replace(imagePath, directUrl)\n    );\n    \n    // 2. Replace in background attributes\n    updatedHtml = updatedHtml.replace(\n      new RegExp(`background=[\"']${escapedPath}[\"']`, 'g'), \n      match => match.replace(imagePath, directUrl)\n    );\n    \n    // 3. Replace in v:fill src attributes (for Outlook)\n    updatedHtml = updatedHtml.replace(\n      new RegExp(`<v:fill[^>]*src=[\"']${escapedPath}[\"'][^>]*>`, 'g'), \n      match => match.replace(imagePath, directUrl)\n    );\n    \n    // 4. Replace in CSS background and background-image styles\n    // Handle various quote styles and syntax patterns\n    const backgroundPatterns = [\n      `background-image:\\\\s*url\\\\(['\"]?${escapedPath}['\"]?\\\\)`,\n      `background:\\\\s*url\\\\(['\"]?${escapedPath}['\"]?\\\\)`,\n      `background:\\\\s*[^;]*url\\\\(['\"]?${escapedPath}['\"]?\\\\)`,\n    ];\n    \n    backgroundPatterns.forEach(pattern => {\n      updatedHtml = updatedHtml.replace(\n        new RegExp(pattern, 'g'),\n        match => match.replace(imagePath, directUrl)\n      );\n    });\n    \n    // 5. Replace in CSS class references\n    // This is more complex and depends on your HTML structure\n    // Extract file name for matching class names\n    const fileNameMatch = imagePath.match(/\\/([^\\/]+)\\.([^\\.]+)$/);\n    if (fileNameMatch) {\n      const fileName = fileNameMatch[1];\n      const fileExt = fileNameMatch[2];\n      \n      // Handle class names like \"bgiurlimagesdiv2dpng\"\n      const classPatterns = [\n        `bgiurlimagesdiv${fileName}d${fileExt}`,\n        `bgurlimagesdiv${fileName}d${fileExt}`\n      ];\n      \n      classPatterns.forEach(pattern => {\n        // Replace the class name and also update the associated styles if needed\n        updatedHtml = updatedHtml.replace(\n          new RegExp(`class=[\"'][^\"']*${pattern}[^\"']*[\"']`, 'g'),\n          match => match.replace(pattern, `direct-url-${Date.now()}`)\n        );\n      });\n    }\n  });\n  \n  return updatedHtml;\n}\n\n// This is the main execution for n8n\n// It processes each item in the incoming array and returns the results\nreturn items.map(processData);"
      },
      "typeVersion": 2
    },
    {
      "id": "0fbdb7d6-64a3-40ae-8187-8afcb76451e8",
      "name": "Embed image in HTML 1",
      "type": "n8n-nodes-base.code",
      "position": [
        1960,
        400
      ],
      "parameters": {
        "jsCode": "// n8n Code Node: HTML Image URL Replacer\n// This script replaces all image references in HTML with their direct URLs\n// It's designed to work with the data structure you provided\n\n/**\n * Main function to process the incoming data in n8n\n * Expects an item with html and medias properties\n */\nfunction processData(item) {\n  // Extract the HTML and media mappings from the item\n  const { html, medias } = item.json;\n\n  // Process the HTML and replace all image references\n  const processedHtml = replaceImageReferences(html, medias);\n\n  // Return the processed data\n  return {\n    json: {\n      ...item.json,\n      html: processedHtml\n    }\n  };\n}\n\n/**\n * Replace all image references in HTML with direct URLs\n * @param {string} html - The HTML content to process\n * @param {Object} medias - Key-value pairs mapping image paths to direct URLs\n * @returns {string} - Processed HTML with direct URLs\n */\nfunction replaceImageReferences(html, medias) {\n  if (!html || !medias) {\n    throw new Error('Missing required parameters: html or medias');\n  }\n  \n  let updatedHtml = html;\n  \n  // Process each media URL\n  Object.entries(medias).forEach(([imagePath, directUrl]) => {\n    // For safety, escape special regex characters in the image path\n    const escapedPath = imagePath.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n    \n    // 1. Replace in img src attributes\n    updatedHtml = updatedHtml.replace(\n      new RegExp(`src=[\"']${escapedPath}[\"']`, 'g'), \n      match => match.replace(imagePath, directUrl)\n    );\n    \n    // 2. Replace in background attributes\n    updatedHtml = updatedHtml.replace(\n      new RegExp(`background=[\"']${escapedPath}[\"']`, 'g'), \n      match => match.replace(imagePath, directUrl)\n    );\n    \n    // 3. Replace in v:fill src attributes (for Outlook)\n    updatedHtml = updatedHtml.replace(\n      new RegExp(`<v:fill[^>]*src=[\"']${escapedPath}[\"'][^>]*>`, 'g'), \n      match => match.replace(imagePath, directUrl)\n    );\n    \n    // 4. Replace in CSS background and background-image styles\n    // Handle various quote styles and syntax patterns\n    const backgroundPatterns = [\n      `background-image:\\\\s*url\\\\(['\"]?${escapedPath}['\"]?\\\\)`,\n      `background:\\\\s*url\\\\(['\"]?${escapedPath}['\"]?\\\\)`,\n      `background:\\\\s*[^;]*url\\\\(['\"]?${escapedPath}['\"]?\\\\)`,\n    ];\n    \n    backgroundPatterns.forEach(pattern => {\n      updatedHtml = updatedHtml.replace(\n        new RegExp(pattern, 'g'),\n        match => match.replace(imagePath, directUrl)\n      );\n    });\n    \n    // 5. Replace in CSS class references\n    // This is more complex and depends on your HTML structure\n    // Extract file name for matching class names\n    const fileNameMatch = imagePath.match(/\\/([^\\/]+)\\.([^\\.]+)$/);\n    if (fileNameMatch) {\n      const fileName = fileNameMatch[1];\n      const fileExt = fileNameMatch[2];\n      \n      // Handle class names like \"bgiurlimagesdiv2dpng\"\n      const classPatterns = [\n        `bgiurlimagesdiv${fileName}d${fileExt}`,\n        `bgurlimagesdiv${fileName}d${fileExt}`\n      ];\n      \n      classPatterns.forEach(pattern => {\n        // Replace the class name and also update the associated styles if needed\n        updatedHtml = updatedHtml.replace(\n          new RegExp(`class=[\"'][^\"']*${pattern}[^\"']*[\"']`, 'g'),\n          match => match.replace(pattern, `direct-url-${Date.now()}`)\n        );\n      });\n    }\n  });\n  \n  return updatedHtml;\n}\n\n// This is the main execution for n8n\n// It processes each item in the incoming array and returns the results\nreturn items.map(processData);"
      },
      "typeVersion": 2
    },
    {
      "id": "89acf2f3-940b-47e9-81e3-2d458d3018ee",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -460,
        80
      ],
      "parameters": {
        "height": 400,
        "content": "## Authentication Set Up\n\nObtain an access token from Dartagnan\nPrepare credentials for both Dartagnan and Braze"
      },
      "typeVersion": 1
    },
    {
      "id": "317cd699-6686-42a0-ba6d-8f6405338356",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        60,
        480
      ],
      "parameters": {
        "color": 5,
        "width": 960,
        "height": 380,
        "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n## Template Discovery\n\n\nList all existing email templates in Braze"
      },
      "typeVersion": 1
    },
    {
      "id": "d59b0dac-439e-4160-8879-b306ccd18773",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "width": 260,
        "height": 380,
        "content": "## Authentication Token\n\nObtain an access token from Dartagnan"
      },
      "typeVersion": 1
    },
    {
      "id": "dc38ec97-ca50-4944-89f1-535f23420cfc",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        300,
        0
      ],
      "parameters": {
        "color": 5,
        "width": 460,
        "height": 380,
        "content": "## Template Discovery\n\nRetrieve project and campaign details from Dartagnan\n"
      },
      "typeVersion": 1
    },
    {
      "id": "5fd7d7dc-f314-411a-ba0a-4af14874f96b",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1040,
        0
      ],
      "parameters": {
        "color": 5,
        "width": 600,
        "height": 680,
        "content": "## Comparison and Sync\n\nCompare Dartagnan templates with existing Braze templates\nIdentify templates to update (top Branch) or create ( Lower Branch)"
      },
      "typeVersion": 1
    },
    {
      "id": "e198dca0-0278-4b67-9807-90796ff2d644",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1660,
        0
      ],
      "parameters": {
        "color": 5,
        "width": 980,
        "height": 680,
        "content": "## Template Processing\n\nExtract HTML and media from Dartagnan templates\nReplace image references with direct URLs\nPrepare templates for Braze\nUpdate existing templates in Braze ( upper branch ) \nCreate new templates in Braze as needed ( lower branch ) "
      },
      "typeVersion": 1
    },
    {
      "id": "321eb50a-66a2-4951-b61b-e0869a91b6a0",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -980,
        80
      ],
      "parameters": {
        "color": 4,
        "width": 460,
        "height": 380,
        "content": "## Trigger\n\nTrigger is scheduled to run every 5 minutes, you can change this, but bear in mind to stay in the API rate limits of each vendor\n"
      },
      "typeVersion": 1
    },
    {
      "id": "8e0984c5-ec70-4ca5-97fe-0bdd24acd95c",
      "name": "Every 5 minutes start",
      "type": "n8n-nodes-base.scheduleTrigger",
      "notes": "This node is a scheduled trigger that will synchronize ever",
      "position": [
        -660,
        300
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "adb2c227-d27e-44fc-8f6e-0b3d02952ef9",
      "name": "Update existing email template in Braze",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2400,
        180
      ],
      "parameters": {
        "url": "=https://{{ $('Assign Credentials').item.json.instance_url }}/templates/email/update ",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"email_template_id\": {{ $('If campaign is modified recently').item.json.campaign_name }},\n  \"template_name\": \"{{ $('If campaign is modified recently').item.json.unified_name }}\",\n  \"subject\": \"Subject Line\",\n  \"body\":{{ $json.encoded_html }} ,\n  \"plaintext_body\": {{ $json.encoded_plaintext_body }}\n} ",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "=Bearer  {{ $('Assign Credentials').item.json.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    }
  ],
  "connections": {
    "Split Out": {
      "main": [
        [
          {
            "node": "Filtering Braze Email Template",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Token Request": {
      "main": [
        [
          {
            "node": "Dartagnan Project list",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Existing In Braze": {
      "main": [
        [
          {
            "node": "If campaign is modified recently",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Assign Credentials": {
      "main": [
        [
          {
            "node": "List Available Email Template Braze",
            "type": "main",
            "index": 0
          },
          {
            "node": "Token Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Embed image in HTML": {
      "main": [
        [
          {
            "node": "Encode Content To Update",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Embed image in HTML 1": {
      "main": [
        [
          {
            "node": "Encode Content to Create",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every 5 minutes start": {
      "main": [
        [
          {
            "node": "Assign Credentials",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Not existing In Braze": {
      "main": [
        [
          {
            "node": "Filter Braze vs Dartagnan",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dartagnan Project list": {
      "main": [
        [
          {
            "node": "Filtered Project Campaign",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Encode Content To Update": {
      "main": [
        [
          {
            "node": "Update existing email template in Braze",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Encode Content to Create": {
      "main": [
        [
          {
            "node": "Create email template",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Braze vs Dartagnan": {
      "main": [
        [
          {
            "node": "Dartagnan HTML & MEDIA Campagne to Create",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filtered Project Campaign": {
      "main": [
        [
          {
            "node": "Filtering Dartagnan Campaigns",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filtering Dartagnan Campaigns": {
      "main": [
        [
          {
            "node": "Existing In Braze",
            "type": "main",
            "index": 0
          },
          {
            "node": "Not existing In Braze",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filtering Braze Email Template": {
      "main": [
        [
          {
            "node": "Existing In Braze",
            "type": "main",
            "index": 1
          },
          {
            "node": "Not existing In Braze",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Dartagnan HTML & MEDIA To Update": {
      "main": [
        [
          {
            "node": "Embed image in HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If campaign is modified recently": {
      "main": [
        [
          {
            "node": "Dartagnan HTML & MEDIA To Update",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "List Available Email Template Braze": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dartagnan HTML & MEDIA Campagne to Create": {
      "main": [
        [
          {
            "node": "Embed image in HTML 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Actions

Share
Categories:
Tags:
ifsetmergehttprequeststickynotecodescheduletriggersplitout

Technical Specs

Complexity:
Expert
Node Count:
28 nodes
Published:11 months ago
Updated:3 months ago

Compatibility

Platform:
N8N Cloud & Self-hosted
Min Version:
N8N v1.0.0+
Supports cloud and self-hosted deployment

License

This workflow template follows MIT license, you can freely use, modify and distribute.

Please comply with relevant third-party service terms when using.

Related Workflows