{
  "openapi": "3.0.3",
  "info": {
    "version": "1.0.0",
    "title": "TOPOL Email Template Conversion API",
    "description": "Convert JSON email template definitions to HTML using MJML rendering engine. This API provides template conversion with font validation, variant handling, and comprehensive error reporting.",
    "contact": {
      "name": "TOPOL.io Support",
      "url": "https://topol.io"
    },
    "license": {
      "name": "MIT"
    }
  },
  "servers": [
    {
      "url": "https://api.topol.io",
      "description": "Production server"
    }
  ],
  "security": [
    {
      "SecretKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Conversion API",
      "description": "Convert email templates from JSON format to HTML. Supports MJML rendering, font validation, and template variants."
    }
  ],
  "components": {
    "securitySchemes": {
      "SecretKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Secret-Key",
        "description": "Secret key for authentication"
      }
    },
    "schemas": {
      "Font": {
        "type": "object",
        "required": ["label", "style", "url"],
        "properties": {
          "label": {
            "type": "string",
            "description": "Display name of the font",
            "example": "Open Sans"
          },
          "style": {
            "type": "string",
            "description": "CSS font-family value",
            "example": "Open Sans, sans-serif"
          },
          "url": {
            "type": "string",
            "format": "url",
            "description": "URL to the font CSS file (must use HTTPS)",
            "example": "https://fonts.googleapis.com/css?family=Open+Sans:400,700"
          }
        }
      },
      "Warning": {
        "type": "object",
        "required": ["type", "message"],
        "properties": {
          "type": {
            "type": "string",
            "enum": ["missing_fonts"],
            "description": "Type of warning",
            "example": "missing_fonts"
          },
          "message": {
            "type": "string",
            "description": "Warning message",
            "example": "Some fonts are missing"
          },
          "missing_fonts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "List of missing font names",
            "example": ["Arial", "Helvetica"]
          }
        }
      },
      "Json2HtmlRequest": {
        "type": "object",
        "required": ["definition", "options"],
        "properties": {
          "definition": {
            "type": "string",
            "description": "JSON template definition",
            "example": "{\"tagName\":\"mj-global-style\",\"attributes\":{\"h1:color\":\"#000000\",\"h1:font-family\":\"Helvetica, Arial, sans-serif\",\"h2:color\":\"#000000\",\"h2:font-family\":\"Helvetica, Arial, sans-serif\",\"h3:color\":\"#000000\",\"h3:font-family\":\"Helvetica, Arial, sans-serif\",\":color\":\"#000000\",\":font-family\":\"Helvetica, Arial, sans-serif\",\":line-height\":\"1.5\",\"a:color\":\"#24bfbc\",\"button:background-color\":\"#e85034\",\"containerWidth\":600,\"fonts\":\"Helvetica,Arial,sans-serif\",\"mj-text\":{\"line-height\":1.5,\"font-size\":15,\"font-family\":\"Helvetica, Arial, sans-serif\"},\"mj-button\":{\"font-size\":\"13px\"}},\"children\":[{\"tagName\":\"mj-body\",\"attributes\":{\"background-color\":\"#FFFFFF\",\"containerWidth\":600},\"children\":[{\"tagName\":\"mj-section\",\"attributes\":{\"full-width\":\"600px\",\"padding\":\"10px 10px\",\"mj-class\":\"section\"},\"children\":[{\"tagName\":\"mj-column\",\"attributes\":{\"width\":\"100%\",\"vertical-align\":\"top\"},\"children\":[{\"uid\":\"9qJHlWzLp\",\"tagName\":\"mj-text\",\"content\":\"<p>This is your new text block with the first paragraph.</p>\",\"attributes\":{\"align\":\"left\",\"padding\":\"15px 15px 15px 15px\",\"line-height\":1.5,\"containerWidth\":600},\"customKey\":null}],\"uid\":\"HJQ8ytZzW\"}],\"layout\":1,\"backgroundColor\":null,\"backgroundImage\":null,\"paddingTop\":0,\"paddingBottom\":0,\"paddingLeft\":0,\"paddingRight\":0,\"uid\":\"Byggju-zb\"}]}],\"style\":{\"h1\":{\"font-family\":\"Helvetica, Arial, sans-serif\",\"font-size\":\"22px\",\"font-weight\":\"bold\"},\"a\":{\"color\":\"#0000EE\"},\"h2\":{\"font-family\":\"Helvetica, Arial, sans-serif\",\"font-size\":\"17px\",\"font-weight\":\"bold\"},\"h3\":{\"font-family\":\"Helvetica, Arial, sans-serif\",\"font-size\":\"13px\",\"font-weight\":\"bold\"},\"p\":{\"font-family\":\"Helvetica, Arial, sans-serif\",\"font-size\":\"11px\"},\"ul\":{\"font-size\":\"11px\",\"font-family\":\"Helvetica, Arial, sans-serif\"},\"li\":{\"font-size\":\"11px\",\"font-family\":\"Helvetica, Arial, sans-serif\"},\"ol\":{\"font-size\":\"11px\",\"font-family\":\"Helvetica, Arial, sans-serif\"},\".rounded>table\":{\"border-collapse\":\"separate\"}},\"fonts\":[\"Helvetica, Arial, sans-serif\"],\"feedBlocksApplyToAll\":{},\"langs\":[{\"key\":\"en\",\"primary\":true,\"translations\":{\"9qJHlWzLp\":{\"content\":\"<p>This is your new text block with the first paragraph.</p>\"}}}]}"
          },
          "options": {
            "type": "object",
            "properties": {
              "minify": {
                "type": "boolean",
                "description": "Whether to minify the output HTML",
                "example": true
              },
              "fonts": {
                "type": "string",
                "description": "JSON string containing an array of font objects with label, style, and url properties",
                "example": "[{\"label\":\"Open Sans\",\"style\":\"Open Sans, sans-serif\",\"url\":\"https://fonts.googleapis.com/css?family=Open+Sans:400,700\"}]"
              },
              "emailServiceProvider": {
                "type": "string",
                "enum": ["mailchimp", "sparkpost"],
                "description": "Target email service provider",
                "example": "mailchimp"
              },
              "mergeTags": {
                "type": "array",
                "description": "Array of merge tags for template variable substitution",
                "example": []
              },
              "evaluateMergeTags": {
                "type": "boolean",
                "description": "Whether to evaluate merge tags in the template",
                "example": false
              },
              "variants": {
                "type": "object",
                "properties": {
                  "evaluate": {
                    "type": "boolean",
                    "description": "Whether to evaluate variants",
                    "example": false
                  },
                  "enrichWithConditions": {
                    "type": "boolean",
                    "description": "Whether to enrich with conditional logic",
                    "example": true
                  }
                }
              },
              "desktopFirst": {
                "type": "boolean",
                "description": "Whether to prioritize desktop layout",
                "example": false
              },
              "language": {
                "type": "string",
                "description": "Template language code",
                "example": "en"
              }
            }
          },
          "syncedSections": {
            "type": "array",
            "description": "Resolved content for every synced section referenced by the template. A section in the template JSON that carries a `syncedId` is stored as a lightweight reference only; the rendering pipeline must be supplied with the matching content here, otherwise the section renders empty. Fetch each referenced synced section from your saved-sections storage (see the Saved Blocks and Synced Sections guide) and pass it in this array before calling this endpoint.",
            "items": {
              "type": "object",
              "required": ["id", "definition"],
              "properties": {
                "id": {
                  "type": "number",
                  "description": "ID of the synced section. Must match the `syncedId` on a section node in the template JSON.",
                  "example": 1
                },
                "definition": {
                  "type": "string",
                  "description": "JSON-encoded MJML definition of the synced section (string, not a nested object)."
                },
                "translation": {
                  "type": "string",
                  "description": "Optional JSON-encoded locale-keyed translation map for multilingual templates. Required if the template has language mutations and the section contains translatable content."
                }
              }
            }
          }
        }
      },
      "Json2HtmlSuccess": {
        "type": "object",
        "required": ["html"],
        "properties": {
          "html": {
            "type": "string",
            "description": "Rendered HTML output",
            "example": "<html><body>Hello World</body></html>"
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Optional warnings about the conversion"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": ["message"],
        "properties": {
          "message": {
            "type": "string",
            "description": "Error message",
            "example": "An error occurred"
          }
        }
      },
      "ValidationErrorResponse": {
        "type": "object",
        "required": ["message", "errors"],
        "properties": {
          "message": {
            "type": "string",
            "description": "Main error message",
            "example": "Validation failed"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string",
                  "description": "Field that failed validation",
                  "example": "definition"
                },
                "message": {
                  "type": "string",
                  "description": "Specific validation error message",
                  "example": "Field is required"
                }
              }
            },
            "description": "List of validation errors"
          }
        }
      }
    }
  },
  "paths": {
    "/email/v1/json2html": {
      "post": {
        "description": "Convert JSON template to HTML using MJML rendering engine. Authentication is done via the X-Secret-Key header.\n\nIf the template contains synced sections (any section node with a `syncedId` property), the matching content must be supplied through the optional `syncedSections` request field. Synced section references are resolved before merge tag evaluation and variant rendering: the endpoint looks up each `syncedId` in the supplied array, splices the section's full MJML into the template, regenerates child block UIDs to avoid collisions, and (for multilingual templates) merges per-locale translations into the template's `langs` array. Without this step, synced sections render as empty placeholders.",
        "summary": "JSON to HTML",
        "operationId": "convertJson2Html",
        "tags": ["Conversion API"],
        "security": [
          {
            "SecretKeyAuth": []
          }
        ],
        "requestBody": {
          "description": "Template conversion request",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Json2HtmlRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Template converted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Json2HtmlSuccess"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing or invalid request body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or expired API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation error - invalid request format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  }
}
