diff --git a/INVESTIGATION_TEMPLATE_HTML_TRANSFORMATION.md b/INVESTIGATION_TEMPLATE_HTML_TRANSFORMATION.md
new file mode 100644
index 0000000..eb4bafb
--- /dev/null
+++ b/INVESTIGATION_TEMPLATE_HTML_TRANSFORMATION.md
@@ -0,0 +1,289 @@
+# Investigation: Template HTML Transformation and Content Duplication
+
+**Related Issues:**
+- [resend-python#186](https://github.com/resend/resend-python/issues/186) - Templates.create() transforms raw HTML
+- [resend-python#187](https://github.com/resend/resend-python/issues/187) - Templates.update() appends HTML instead of replacing it
+- Linear: PRODUCT-1427 - Opening template rewrites HTML and duplicates content
+
+**Investigation Date:** February 20, 2026
+
+## Summary
+
+The Resend dashboard template editor processes raw HTML through a React Email rendering pipeline and persists the transformed result back to storage **every time a template is opened**, even when no edits are made. This causes:
+
+1. **HTML Transformation:** Original HTML gets bloated with React Email artifacts (data-id attributes, HTML comments, inline styles)
+2. **Content Duplication:** Subsequent updates via API cause content to be duplicated in the editor
+3. **Unexpected Behavior:** HTML sent via API differs from what's retrieved after dashboard viewing
+
+## Root Cause Analysis
+
+### The Problem Flow
+
+1. **Create Template via API** (3,267 chars of HTML)
+ ```python
+ resend.Templates.create({
+ "name": "test-template",
+ "html": "..." # Raw HTML
+ })
+ ```
+
+2. **GET /templates/{id}** → Returns identical HTML (3,267 chars) ✅
+
+3. **Open Template in Dashboard** (no edits, just view it)
+ - Dashboard renders HTML through React Email pipeline
+ - React Email components add artifacts:
+ - `data-id="__react-email-column"` attributes
+ - `` and `` comment markers
+ - Injected inline styles
+ - Transformed HTML structure
+ - **Dashboard saves transformed HTML back to storage** ❌
+
+4. **GET /templates/{id}** → HTML now ~21,000+ chars ❌
+
+5. **Update via API with original HTML**
+ ```python
+ resend.Templates.update({
+ "id": template_id,
+ "html": "..." # Same original HTML
+ })
+ ```
+
+6. **Open Dashboard Again** → Content appears duplicated ❌
+
+### Source of Artifacts
+
+Investigation of the [react-email](https://github.com/resend/react-email) repository reveals React Email components hardcode `data-id` attributes:
+
+**packages/column/src/column.tsx:**
+```typescript
+export const Column = React.forwardRef(
+ ({ children, style, ...props }, ref) => {
+ return (
+
+```
+
+When raw HTML is rendered through React Email (even just for viewing), these components inject their attributes and transform the HTML structure.
+
+## Evidence
+
+### Reproduction Script
+
+A [Node.js reproduction script](https://gist.github.com/drish/5352959c0dca9abd96141b301ea317a1) demonstrates:
+
+- API behavior is correct (PATCH replaces HTML exactly as sent)
+- Transformation only occurs after opening template in dashboard
+- Issue affects both Python SDK and Node.js SDK identically
+- **Conclusion: Platform/dashboard issue, not SDK-specific**
+
+### Example Transformation
+
+**Original HTML:**
+```html
+
+
+
Cell 1
+
Cell 2
+
+
+```
+
+**After Dashboard View:**
+```html
+
+
+
Cell 1
+
Cell 2
+
+
+```
+
+## Impact
+
+### User-Reported Problems
+
+1. **HTML Bloat:** Templates grow 6-7x in size after dashboard viewing
+2. **Content Duplication:** Updates cause content to appear multiple times
+3. **Unexpected Behavior:** HTML sent via API ≠ HTML retrieved after dashboard interaction
+4. **Loss of Control:** Users lose ability to maintain exact HTML structure
+
+### Affected Users
+
+- Users creating templates via API with custom HTML
+- Users expecting API to preserve exact HTML formatting
+- Users with CSS that relies on specific HTML structure
+- Users with dark mode or complex styling (as reported in issues)
+
+## Proposed Solutions
+
+### Solution 1: Prevent Auto-Save on View (Recommended)
+
+**Location:** Dashboard template editor code (private repository)
+
+**Fix:** Only persist HTML changes when user explicitly saves, not on template view/load.
+
+```typescript
+// Current (problematic):
+function onTemplateLoad(template) {
+ const transformed = renderThroughReactEmail(template.html);
+ saveToStorage(transformed); // ❌ Don't do this
+ displayInEditor(transformed);
+}
+
+// Proposed:
+function onTemplateLoad(template) {
+ const transformed = renderThroughReactEmail(template.html);
+ displayInEditor(transformed); // ✅ Only display, don't save
+}
+
+function onUserSave(editorContent) {
+ saveToStorage(editorContent); // ✅ Only save on explicit user action
+}
+```
+
+**Benefits:**
+- Preserves original HTML until user makes intentional changes
+- Fixes both transformation and duplication issues
+- No SDK changes required
+
+### Solution 2: Add "plainText" Mode to Dashboard
+
+**Location:** Dashboard template editor code
+
+**Fix:** When loading templates created via API (not through React Email editor), display in plain HTML mode without React Email transformation.
+
+```typescript
+function onTemplateLoad(template) {
+ if (template.createdViaAPI && !template.usesReactEmailComponents) {
+ displayAsPlainHTML(template.html); // No transformation
+ } else {
+ const transformed = renderThroughReactEmail(template.html);
+ displayInEditor(transformed);
+ }
+}
+```
+
+**Benefits:**
+- Preserves API-created templates
+- Allows React Email templates to work as expected
+- Clear distinction between template types
+
+### Solution 3: Add Render Options to React Email
+
+**Location:** react-email repository
+
+**Fix:** Add option to skip adding `data-id` attributes during rendering.
+
+```typescript
+export const Column = React.forwardRef(
+ ({ children, style, includeDataId = true, ...props }, ref) => {
+ const dataIdProp = includeDataId ? { 'data-id': '__react-email-column' } : {};
+ return (
+
+ {children}
+
+ );
+ },
+);
+```
+
+**Benefits:**
+- Allows clean HTML output when needed
+- Maintains editor functionality when attributes are included
+- Flexible for different use cases
+
+**Drawbacks:**
+- Requires changes across multiple React Email components
+- May affect editor features that rely on these attributes
+
+### Solution 4: Template Versioning
+
+**Location:** Dashboard backend
+
+**Fix:** Store both original and transformed versions, serve appropriate version based on request source.
+
+```typescript
+interface Template {
+ id: string;
+ name: string;
+ originalHtml: string; // Preserved as submitted via API
+ transformedHtml?: string; // For dashboard editor use
+ // ...
+}
+```
+
+**Benefits:**
+- Preserves original HTML for API consumers
+- Allows dashboard to work with transformed version
+- Non-breaking change
+
+**Drawbacks:**
+- Increased storage requirements
+- More complex synchronization logic
+
+## Recommended Action Plan
+
+1. **Immediate Fix (Critical):** Implement Solution 1 - Stop auto-saving transformed HTML on template view
+2. **Short-term:** Implement Solution 2 - Add plain HTML mode for API-created templates
+3. **Long-term:** Consider Solution 4 - Template versioning for robust handling of both API and UI workflows
+
+## SDK Considerations
+
+The Python SDK (and all other SDKs) are working correctly. No SDK changes are required. The issue is entirely in the dashboard/backend platform code.
+
+### Workaround for Users (Until Fix)
+
+Currently, users should:
+1. Create/update templates via API
+2. **Avoid opening templates in dashboard** if HTML preservation is critical
+3. Use the Resend API directly for all template operations
+
+## Testing Recommendations
+
+After implementing fixes, verify:
+
+1. ✅ Template created via API → HTML unchanged after dashboard view
+2. ✅ Template updated via API → No content duplication
+3. ✅ Template created in dashboard → React Email features work correctly
+4. ✅ Template edited in dashboard → Changes saved correctly
+5. ✅ API GET returns exact HTML that was PUT/PATCH
+
+## Related Code Repositories
+
+- **resend-python:** SDK confirmed working correctly
+- **resend-node:** SDK confirmed working correctly
+- **react-email:** Contains components that add data-id attributes
+- **resend-openapi:** API specification (does not indicate transformation behavior)
+- **Dashboard (private):** Contains problematic auto-save logic
+
+## Verification
+
+To verify the fix works:
+
+```bash
+# Run the reproduction script from the gist
+node reproduction-script.js
+
+# Expected results after fix:
+# - Original HTML length: 3267 chars
+# - After dashboard view: 3267 chars (unchanged)
+# - After update: 3267 chars (unchanged)
+# - HTML identical to original: true (always)
+```
+
+## Contributors
+
+- **Investigation:** João (drish) - identified root cause
+- **Reproduction:** Node.js script demonstrating issue
+- **Analysis:** Cursor AI Agent - traced issue to React Email component attributes and dashboard auto-save behavior
diff --git a/TEMPLATE_ISSUE_README.md b/TEMPLATE_ISSUE_README.md
new file mode 100644
index 0000000..638cd05
--- /dev/null
+++ b/TEMPLATE_ISSUE_README.md
@@ -0,0 +1,71 @@
+# Template HTML Transformation Issue
+
+## Quick Links
+
+- **Full Investigation Report:** [INVESTIGATION_TEMPLATE_HTML_TRANSFORMATION.md](./INVESTIGATION_TEMPLATE_HTML_TRANSFORMATION.md)
+- **Python Reproduction Script:** [examples/investigation_template_transformation.py](./examples/investigation_template_transformation.py)
+- **Related Issues:** [#186](https://github.com/resend/resend-python/issues/186), [#187](https://github.com/resend/resend-python/issues/187)
+
+## TL;DR
+
+**Problem:** Opening a template in the Resend dashboard causes the HTML to be transformed through React Email and saved back to storage, even when no edits are made. This results in:
+- HTML bloat (6-7x size increase)
+- Content duplication on subsequent updates
+- Loss of exact HTML formatting
+
+**Root Cause:** Dashboard auto-saves React Email-transformed HTML (with `data-id` attributes and comments) instead of preserving the original HTML.
+
+**Status:** Confirmed platform issue, not an SDK bug. The Python SDK is working correctly.
+
+**Workaround:** Avoid opening templates in the dashboard if HTML preservation is critical. Use API-only operations.
+
+## Running the Investigation
+
+To reproduce the issue yourself:
+
+```bash
+export RESEND_API_KEY="re_your_api_key"
+python examples/investigation_template_transformation.py
+```
+
+The script will:
+1. Create a template with complex HTML (3,267 chars)
+2. Verify HTML is preserved after creation
+3. Prompt you to open the template in dashboard
+4. Show HTML has been transformed (~21,000+ chars)
+5. Demonstrate content duplication on updates
+
+## Proposed Solutions
+
+See the [full investigation report](./INVESTIGATION_TEMPLATE_HTML_TRANSFORMATION.md#proposed-solutions) for detailed solutions, including:
+
+1. **Stop auto-saving transformed HTML** (recommended immediate fix)
+2. **Add plain HTML mode** for API-created templates
+3. **Make React Email data-id attributes optional**
+4. **Template versioning** to store both original and transformed HTML
+
+## For Resend Team
+
+The fix needs to be implemented in the dashboard/platform code (likely a private repository). The specific area to investigate:
+
+```typescript
+// Dashboard template editor (pseudo-code)
+function onTemplateLoad(template) {
+ const transformed = renderThroughReactEmail(template.html);
+ saveToStorage(transformed); // ❌ This line is the problem
+ displayInEditor(transformed);
+}
+```
+
+Should be:
+
+```typescript
+function onTemplateLoad(template) {
+ const transformed = renderThroughReactEmail(template.html);
+ displayInEditor(transformed); // ✅ Display only, don't auto-save
+}
+```
+
+## SDK Impact
+
+**No SDK changes are required.** All SDKs (Python, Node.js, etc.) are functioning correctly. The issue is entirely in the platform/dashboard layer.
diff --git a/examples/investigation_template_transformation.py b/examples/investigation_template_transformation.py
new file mode 100644
index 0000000..d5e1ef0
--- /dev/null
+++ b/examples/investigation_template_transformation.py
@@ -0,0 +1,320 @@
+"""
+Investigation Script: Template HTML Transformation Issue
+
+This script demonstrates the issue where opening a template in the Resend dashboard
+causes the HTML to be transformed through React Email pipeline and saved back to storage.
+
+Related Issues:
+- https://github.com/resend/resend-python/issues/186
+- https://github.com/resend/resend-python/issues/187
+- Linear: PRODUCT-1427
+
+Usage:
+ export RESEND_API_KEY="re_..."
+ python examples/investigation_template_transformation.py
+"""
+
+import os
+import time
+from typing import Optional
+
+import resend
+
+# Same HTML used in the Node.js investigation
+COMPLEX_HTML = """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Monthly Report
+
Borders are blue in light mode and red in dark mode.
+
+
+
+
+
+
+
Metric
+
Value
+
Change
+
+
+
Emails Sent
+
12,450
+
+18%
+
+
+
Open Rate
+
42.3%
+
+2.1%
+
+
+
Click Rate
+
8.7%
+
-0.4%
+
+
+
Bounces
+
23
+
-12
+
+
+
+
+
+
+
+
+
+"""
+
+
+def find_first_diff(a: str, b: str) -> Optional[int]:
+ """Find the first character position where two strings differ."""
+ min_len = min(len(a), len(b))
+ for i in range(min_len):
+ if a[i] != b[i]:
+ return i
+ return min_len if len(a) != len(b) else None
+
+
+def pause(message: str) -> None:
+ """Pause execution and wait for user input."""
+ print("\n" + "=" * 60)
+ print(f"PAUSE: {message}")
+ print("=" * 60)
+ input("Press Enter to continue...\n")
+
+
+def main():
+ """Run the investigation."""
+ # Ensure API key is set
+ resend.api_key = os.environ.get("RESEND_API_KEY")
+ if not resend.api_key:
+ print("ERROR: RESEND_API_KEY environment variable not set")
+ return
+
+ html_length = len(COMPLEX_HTML)
+ print(f"Original HTML length: {html_length} chars\n")
+
+ # Step 1: Create template
+ print("--- Step 1: Creating template ---")
+ timestamp = int(time.time())
+ create_params: resend.Templates.CreateParams = {
+ "name": f"Investigation Test {timestamp}",
+ "subject": "Welcome",
+ "html": COMPLEX_HTML,
+ }
+
+ created = resend.Templates.create(create_params)
+ template_id = created["id"]
+ print(f"Created template ID: {template_id}")
+
+ # Step 2: Retrieve via API and compare
+ print("\n--- Step 2: Retrieving via API ---")
+ fetched = resend.Templates.get(template_id)
+
+ fetched_length = len(fetched["html"])
+ html_match = fetched["html"] == COMPLEX_HTML
+ print(f"Retrieved HTML length: {fetched_length} chars")
+ print(f"HTML identical to original: {html_match}")
+
+ if not html_match:
+ print("\nDIFFERENCES FOUND:")
+ diff_pos = find_first_diff(COMPLEX_HTML, fetched["html"])
+ print(f"First divergence at char: {diff_pos}")
+
+ # Step 3: Pause to check dashboard
+ pause(
+ "Open the template in the Resend dashboard editor, then press Enter. "
+ "The dashboard should show TRANSFORMED HTML (React Email artifacts)."
+ )
+
+ # Step 3b: Retrieve AFTER dashboard view to capture the damage
+ print("--- Step 3b: Retrieving via API AFTER dashboard view ---")
+ after_dashboard = resend.Templates.get(template_id)
+ after_dashboard_length = len(after_dashboard["html"])
+ after_dashboard_match = after_dashboard["html"] == COMPLEX_HTML
+ print(f"After-dashboard HTML length: {after_dashboard_length} chars")
+ print(f"HTML identical to original: {after_dashboard_match}")
+
+ if not after_dashboard_match:
+ increase = after_dashboard_length / html_length
+ print(
+ f"Dashboard MUTATED stored HTML: {html_length} -> {after_dashboard_length} chars "
+ f"({increase:.1f}x increase)"
+ )
+
+ # Check for React Email artifacts
+ if "data-id" in after_dashboard["html"]:
+ print(" → Detected 'data-id' attributes (React Email artifact)")
+ if "" in after_dashboard["html"]:
+ print(" → Detected '' comments (React Email artifact)")
+ if "__react-email-column" in after_dashboard["html"]:
+ print(" → Detected '__react-email-column' (React Email artifact)")
+
+ # Step 4: Update with the same HTML
+ print("\n--- Step 4: Updating template with same HTML ---")
+ update_params: resend.Templates.UpdateParams = {
+ "id": template_id,
+ "html": COMPLEX_HTML,
+ }
+ resend.Templates.update(update_params)
+ print("Update response completed")
+
+ # Retrieve again to verify
+ after_update = resend.Templates.get(template_id)
+ after_update_length = len(after_update["html"])
+ after_update_match = after_update["html"] == COMPLEX_HTML
+ print(f"After-update HTML length: {after_update_length} chars")
+ print(f"HTML identical to original: {after_update_match}")
+
+ if not after_update_match:
+ print("\nDIFFERENCES FOUND:")
+ diff_pos = find_first_diff(COMPLEX_HTML, after_update["html"])
+ print(f"First divergence at char: {diff_pos}")
+
+ # Step 5: Pause to check dashboard for duplication
+ pause(
+ "Check the Resend dashboard again. The template should now show DUPLICATED content "
+ "(full body repeated, separated by
)."
+ )
+
+ # Step 6: Second update to check compounding
+ print("--- Step 6: Second update (checking compounding) ---")
+ resend.Templates.update(update_params)
+
+ after_update2 = resend.Templates.get(template_id)
+ after_update2_length = len(after_update2["html"])
+ after_update2_match = after_update2["html"] == COMPLEX_HTML
+ print(f"After 2nd update HTML length: {after_update2_length} chars")
+ print(f"HTML identical to original: {after_update2_match}")
+
+ # Summary
+ print("\n" + "=" * 60)
+ print("SUMMARY")
+ print("=" * 60)
+ print(f"Original HTML: {html_length} chars")
+ print(f"After create (API): {fetched_length} chars - Match: {html_match}")
+ print(
+ f"After dashboard view: {after_dashboard_length} chars - Match: {after_dashboard_match}"
+ )
+ print(
+ f"After update (API): {after_update_length} chars - Match: {after_update_match}"
+ )
+ print(
+ f"After 2nd update: {after_update2_length} chars - Match: {after_update2_match}"
+ )
+
+ if not after_dashboard_match:
+ increase = after_dashboard_length / html_length
+ print(f"\nKEY FINDING: Opening the dashboard editor mutated stored HTML")
+ print(
+ f"from {html_length} to {after_dashboard_length} chars ({increase:.1f}x increase)."
+ )
+ print("The API never transforms HTML — the dashboard's React Email")
+ print("rendering layer rewrites stored data on every view.")
+
+ if html_match and after_update_match and after_update2_match:
+ print("\nAPI behavior is correct — PATCH replaces HTML exactly as sent.")
+ print(
+ "Both #186 and #187 are caused by the dashboard editor, not the API or SDK."
+ )
+
+ # Step 7: Cleanup
+ print("\n--- Cleanup: Removing template ---")
+ try:
+ resend.Templates.remove(template_id)
+ print("Template removed successfully.")
+ except Exception as e:
+ print(f"Remove failed: {e}")
+
+
+if __name__ == "__main__":
+ main()