Question
Why are HTML tags rendered as raw plain text in outbound emails sent via the backend Twilio Conversations REST API, and how can I send HTML-formatted emails on the Conversations email channel?
Product
Twilio Conversations Classic - Twilio Flex
Answer
Passing an HTML string directly into the standard body parameter of the Conversations API (client.conversations.v1.conversations(sid).messages.create) causes Twilio to deliver the content as plain text, displaying raw HTML tags in the recipient's inbox.
To send formatted HTML emails via the Conversations API, you must upload the HTML content as a media asset to the Media Content Service (MCS) with Category=body and attach the returned MediaSid when creating the message:
- Upload the HTML Body to the Media Content Service (MCS)
Send an HTTP POST request to the MCS endpoint, setting Category=body as a query parameter and Content-Type: text/html in the header
async function uploadBody(html, contentType) {
const url = `https://mcs.${MCS_REGION}.twilio.com/v1/Services/${CONVERSATIONS_SERVICE_SID}/Media?Category=body`;
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: authHeader(),
"Content-Type": contentType,
},
body: html,
});
if (!response.ok) {
throw new Error(`Media upload failed (${response.status}): ${await response.text()}`);
}
const media = await response.json();
return media.sid;
}
// Upload HTML content
const htmlMediaSid = await uploadBody(htmlContent, "text/html");
- (Optional) Upload a Plain-Text fallback to MCS to include a plain-text alternative alongside the HTML email
// Upload plain text fallback content
const textMediaSid = await uploadBody(plainTextContent, "text/plain");- Create the Conversation Message with MediaSid(s)
async function createMessage(mediaSids, subjectText) {
const url = `https://conversations.twilio.com/v1/Conversations/${CONVERSATION_SID}/Messages`;
const params = new URLSearchParams();
mediaSids.forEach((sid) => params.append("MediaSid", sid));
if (subjectText) {
params.append("Subject", subjectText);
}
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: authHeader(),
"Content-Type": "application/x-www-form-urlencoded",
},
body: params,
});
if (!response.ok) {
throw new Error(`Message create failed (${response.status}): ${await response.text()}`);
}
return response.json();
}
// Create message attached with MediaSid(s)
const message = await createMessage([htmlMediaSid], "My Email Subject");Additional Information
Setting `Category=body` during the MCS upload is required so Twilio renders the content directly as the email body rather than sending it as a file attachment.