chore: more front end changes

This commit is contained in:
Muhsin Keloth
2025-07-30 13:39:48 +04:00
parent 533ae33199
commit c2b03d68f3
5 changed files with 235 additions and 1502 deletions
@@ -1,4 +1,4 @@
<script>
<script setup>
/**
* This component handles parsing and sending WhatsApp message templates.
* It works as follows:
@@ -11,223 +11,201 @@
import { ref, computed, onMounted } from 'vue';
import { useVuelidate } from '@vuelidate/core';
import { requiredIf } from '@vuelidate/validators';
import { useI18n } from 'vue-i18n';
import Input from 'dashboard/components-next/input/Input.vue';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
NextButton,
// eslint-disable-next-line vue/no-reserved-component-names
Input,
const props = defineProps({
template: {
type: Object,
default: () => ({}),
},
props: {
template: {
type: Object,
default: () => ({}),
});
const emit = defineEmits(['sendMessage', 'resetTemplate']);
const { t } = useI18n();
const processVariable = str => {
return str.replace(/{{|}}/g, '');
};
const allKeysRequired = value => {
const keys = Object.keys(value);
return keys.every(key => value[key]);
};
const processedParams = ref({});
const templateString = computed(() => {
return props.template.components.find(component => component.type === 'BODY')
.text;
});
const headerComponent = computed(() => {
return props.template.components.find(
component => component.type === 'HEADER'
);
});
const hasMediaHeader = computed(() => {
return (
headerComponent.value &&
headerComponent.value.format &&
['IMAGE', 'VIDEO', 'DOCUMENT'].includes(headerComponent.value.format)
);
});
const variables = computed(() => {
return templateString.value.match(/{{([^}]+)}}/g);
});
const processedString = computed(() => {
return templateString.value.replace(/{{([^}]+)}}/g, (match, variable) => {
const variableKey = processVariable(variable);
return processedParams.value[variableKey] || `{{${variable}}}`;
});
});
const v$ = useVuelidate(
{
processedParams: {
requiredIfKeysPresent: requiredIf(variables),
allKeysRequired,
},
},
emits: ['sendMessage', 'resetTemplate'],
setup(props, { emit }) {
const processVariable = str => {
return str.replace(/{{|}}/g, '');
};
{ processedParams }
);
const allKeysRequired = value => {
const keys = Object.keys(value);
return keys.every(key => value[key]);
};
const generateVariables = () => {
const allVariables = {};
const processedParams = ref({});
const templateString = computed(() => {
return props.template.components.find(
component => component.type === 'BODY'
).text;
// Process body variables
const matchedVariables = templateString.value.match(/{{([^}]+)}}/g);
if (matchedVariables) {
allVariables.body = {};
matchedVariables.forEach(variable => {
const key = processVariable(variable);
// Special handling for authentication templates
if (props.template?.category === 'AUTHENTICATION') {
if (
key === '1' ||
key.toLowerCase().includes('otp') ||
key.toLowerCase().includes('code')
) {
allVariables.body.otp_code = '';
} else if (
key === '2' ||
key.toLowerCase().includes('expiry') ||
key.toLowerCase().includes('minute')
) {
allVariables.body.expiry_minutes = '';
} else {
allVariables.body[key] = '';
}
} else {
allVariables.body[key] = '';
}
});
}
const headerComponent = computed(() => {
return props.template.components.find(
component => component.type === 'HEADER'
);
});
// Add media URL field if template has media header
if (hasMediaHeader.value) {
if (!allVariables.header) allVariables.header = {};
allVariables.header.media_url = '';
allVariables.header.media_type = headerComponent.value.format.toLowerCase();
}
const hasMediaHeader = computed(() => {
return (
headerComponent.value &&
headerComponent.value.format &&
['IMAGE', 'VIDEO', 'DOCUMENT'].includes(headerComponent.value.format)
);
});
// Process button variables
const buttonComponents = props.template.components.filter(
component => component.type === 'BUTTONS'
);
const variables = computed(() => {
return templateString.value.match(/{{([^}]+)}}/g);
});
const processedString = computed(() => {
return templateString.value.replace(/{{([^}]+)}}/g, (match, variable) => {
const variableKey = processVariable(variable);
return processedParams.value[variableKey] || `{{${variable}}}`;
});
});
const v$ = useVuelidate(
{
processedParams: {
requiredIfKeysPresent: requiredIf(variables),
allKeysRequired,
},
},
{ processedParams }
);
const generateVariables = () => {
const allVariables = {};
// Process body variables
const matchedVariables = templateString.value.match(/{{([^}]+)}}/g);
if (matchedVariables) {
allVariables.body = {};
matchedVariables.forEach(variable => {
const key = processVariable(variable);
// Special handling for authentication templates
if (props.template?.category === 'AUTHENTICATION') {
if (
key === '1' ||
key.toLowerCase().includes('otp') ||
key.toLowerCase().includes('code')
) {
allVariables.body.otp_code = '';
} else if (
key === '2' ||
key.toLowerCase().includes('expiry') ||
key.toLowerCase().includes('minute')
) {
allVariables.body.expiry_minutes = '';
} else {
allVariables.body[key] = '';
buttonComponents.forEach(buttonComponent => {
if (buttonComponent.buttons) {
buttonComponent.buttons.forEach((button, index) => {
// Skip button parameter inputs for authentication templates
// as they are auto-populated with OTP codes
if (props.template?.category !== 'AUTHENTICATION') {
// Handle URL buttons with variables
if (
button.type === 'URL' &&
button.url &&
button.url.includes('{{')
) {
const buttonVars = button.url.match(/{{([^}]+)}}/g) || [];
if (buttonVars.length > 0) {
if (!allVariables.buttons) allVariables.buttons = [];
allVariables.buttons[index] = {
type: 'url',
parameter: '',
url: button.url,
variables: buttonVars.map(v => processVariable(v)),
};
}
} else {
allVariables.body[key] = '';
}
});
}
// Add media URL field if template has media header
if (hasMediaHeader.value) {
if (!allVariables.header) allVariables.header = {};
allVariables.header.media_url = '';
allVariables.header.media_type =
headerComponent.value.format.toLowerCase();
}
// Process button variables
const buttonComponents = props.template.components.filter(
component => component.type === 'BUTTONS'
);
buttonComponents.forEach(buttonComponent => {
if (buttonComponent.buttons) {
buttonComponent.buttons.forEach((button, index) => {
// Skip button parameter inputs for authentication templates
// as they are auto-populated with OTP codes
if (props.template?.category !== 'AUTHENTICATION') {
// Handle URL buttons with variables
if (
button.type === 'URL' &&
button.url &&
button.url.includes('{{')
) {
const buttonVars = button.url.match(/{{([^}]+)}}/g) || [];
if (buttonVars.length > 0) {
if (!allVariables.buttons) allVariables.buttons = [];
allVariables.buttons[index] = {
type: 'url',
parameter: '',
url: button.url,
variables: buttonVars.map(v => processVariable(v)),
};
}
}
// Handle copy code buttons
if (button.type === 'COPY_CODE') {
if (!allVariables.buttons) allVariables.buttons = [];
allVariables.buttons[index] = {
type: 'copy_code',
parameter: '',
};
}
}
});
// Handle copy code buttons
if (button.type === 'COPY_CODE') {
if (!allVariables.buttons) allVariables.buttons = [];
allVariables.buttons[index] = {
type: 'copy_code',
parameter: '',
};
}
}
});
}
});
processedParams.value = allVariables;
};
const resetTemplate = () => {
emit('resetTemplate');
};
const updateMediaUrl = value => {
if (!processedParams.value.header) {
processedParams.value.header = {};
}
processedParams.value.header.media_url = value;
};
const sendMessage = () => {
v$.value.$touch();
if (v$.value.$invalid) return;
// Auto-populate button parameters for authentication templates
const finalParams = { ...processedParams.value };
if (
props.template?.category === 'AUTHENTICATION' &&
finalParams.buttons
) {
finalParams.buttons.forEach((button, index) => {
if (button.type === 'url') {
// For authentication templates, auto-populate URL button parameter with OTP
if (finalParams.body?.['1']) {
finalParams.buttons[index].parameter = finalParams.body['1'];
} else if (finalParams.body?.otp_code) {
finalParams.buttons[index].parameter = finalParams.body.otp_code;
}
}
});
}
const payload = {
message: processedString.value,
templateParams: {
name: props.template.name,
category: props.template.category,
language: props.template.language,
namespace: props.template.namespace,
processed_params: finalParams,
},
};
emit('sendMessage', payload);
};
onMounted(generateVariables);
return {
processedParams,
variables,
templateString,
processedString,
headerComponent,
hasMediaHeader,
v$,
resetTemplate,
sendMessage,
updateMediaUrl,
};
},
processedParams.value = allVariables;
};
const resetTemplate = () => {
emit('resetTemplate');
};
const updateMediaUrl = value => {
if (!processedParams.value.header) {
processedParams.value.header = {};
}
processedParams.value.header.media_url = value;
};
const sendMessage = () => {
v$.value.$touch();
if (v$.value.$invalid) return;
// Auto-populate button parameters for authentication templates
const finalParams = { ...processedParams.value };
if (props.template?.category === 'AUTHENTICATION' && finalParams.buttons) {
finalParams.buttons.forEach((button, index) => {
if (button.type === 'url') {
// For authentication templates, auto-populate URL button parameter with OTP
if (finalParams.body?.['1']) {
finalParams.buttons[index].parameter = finalParams.body['1'];
} else if (finalParams.body?.otp_code) {
finalParams.buttons[index].parameter = finalParams.body.otp_code;
}
}
});
}
const payload = {
message: processedString.value,
templateParams: {
name: props.template.name,
category: props.template.category,
language: props.template.language,
namespace: props.template.namespace,
processed_params: finalParams,
},
};
emit('sendMessage', payload);
};
onMounted(generateVariables);
</script>
<template>
@@ -241,6 +219,7 @@ export default {
{{ template.name }}
</h3>
<span class="text-xs text-n-slate-11">
{{ t('WHATSAPP_TEMPLATES.PARSER.LANGUAGE') }}:
{{ template.language || 'en' }}
</span>
</div>
@@ -254,12 +233,12 @@ export default {
</div>
<div class="text-xs text-n-slate-11">
{{ t('WHATSAPP_TEMPLATES.PARSER.CATEGORY') }}:
{{ template.category || 'UTILITY' }}
</div>
</div>
<div v-if="variables || hasMediaHeader">
<!-- Media Header Section -->
<div v-if="hasMediaHeader" class="mb-4">
<p class="mb-2.5 text-sm font-semibold">
{{
@@ -279,7 +258,13 @@ export default {
:model-value="processedParams.header?.media_url || ''"
type="url"
class="flex-1"
:placeholder="`Enter ${headerComponent.format.toLowerCase()} URL`"
:placeholder="
t('WHATSAPP_TEMPLATES.PARSER.MEDIA_URL_LABEL', {
type:
headerComponent.format.charAt(0) +
headerComponent.format.slice(1).toLowerCase(),
})
"
@update:model-value="updateMediaUrl"
/>
</div>
@@ -310,10 +295,12 @@ export default {
class="flex-1"
:placeholder="
key === 'otp_code'
? 'Enter 4-8 digit OTP'
? t('WHATSAPP_TEMPLATES.PARSER.OTP_CODE')
: key === 'expiry_minutes'
? 'Enter expiry minutes'
: `Enter ${key} value`
? t('WHATSAPP_TEMPLATES.PARSER.EXPIRY_MINUTES')
: t('WHATSAPP_TEMPLATES.PARSER.VARIABLE_PLACEHOLDER', {
variable: key,
})
"
/>
</div>
@@ -322,10 +309,7 @@ export default {
<!-- Button Variables Section -->
<div v-if="processedParams.buttons">
<p class="mb-2.5 text-sm font-semibold">
{{
$t('WHATSAPP_TEMPLATES.PARSER.BUTTON_PARAMETERS') ||
'Button Parameters'
}}
{{ t('WHATSAPP_TEMPLATES.PARSER.BUTTON_PARAMETERS') }}
</p>
<div
v-for="(button, index) in processedParams.buttons"
@@ -339,8 +323,8 @@ export default {
class="flex-1"
:placeholder="
button.type === 'copy_code'
? 'Enter coupon code (max 15 chars)'
: 'Enter button parameter'
? t('WHATSAPP_TEMPLATES.PARSER.COUPON_CODE')
: t('WHATSAPP_TEMPLATES.PARSER.BUTTON_PARAMETER')
"
/>
</div>
@@ -1,42 +1,45 @@
{
"WHATSAPP_TEMPLATES": {
"MODAL": {
"TITLE": "Whatsapp Templates",
"SUBTITLE": "Select the whatsapp template you want to send",
"TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
},
"PICKER": {
"SEARCH_PLACEHOLDER": "Search Templates",
"NO_TEMPLATES_FOUND": "No templates found for",
"HEADER": "Header",
"BODY": "Body",
"FOOTER": "Footer",
"BUTTONS": "Buttons",
"CATEGORY": "Category",
"MEDIA_CONTENT": "Media Content",
"NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
"REFRESH_BUTTON": "Refresh templates",
"REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
"REFRESH_ERROR": "Failed to refresh templates. Please try again.",
"LABELS": {
"LANGUAGE": "Language",
"TEMPLATE_BODY": "Template Body",
"CATEGORY": "Category"
}
},
"PARSER": {
"VARIABLES_LABEL": "Variables",
"VARIABLE_PLACEHOLDER": "Enter {variable} value",
"GO_BACK_LABEL": "Go Back",
"SEND_MESSAGE_LABEL": "Send Message",
"FORM_ERROR_MESSAGE": "Please fill all variables before sending",
"MEDIA_HEADER_LABEL": "{type} Header",
"MEDIA_URL_LABEL": "{type} URL",
"OTP_CODE": "OTP Code",
"EXPIRY_MINUTES": "Expiry (minutes)",
"BUTTON_PARAMETERS": "Button Parameters",
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Coupon Code"
}
"WHATSAPP_TEMPLATES": {
"MODAL": {
"TITLE": "Whatsapp Templates",
"SUBTITLE": "Select the whatsapp template you want to send",
"TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
},
"PICKER": {
"SEARCH_PLACEHOLDER": "Search Templates",
"NO_TEMPLATES_FOUND": "No templates found for",
"HEADER": "Header",
"BODY": "Body",
"FOOTER": "Footer",
"BUTTONS": "Buttons",
"CATEGORY": "Category",
"MEDIA_CONTENT": "Media Content",
"NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
"REFRESH_BUTTON": "Refresh templates",
"REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
"REFRESH_ERROR": "Failed to refresh templates. Please try again.",
"LABELS": {
"LANGUAGE": "Language",
"TEMPLATE_BODY": "Template Body",
"CATEGORY": "Category"
}
},
"PARSER": {
"VARIABLES_LABEL": "Variables",
"LANGUAGE": "Language",
"CATEGORY": "Category",
"VARIABLE_PLACEHOLDER": "Enter {variable} value",
"GO_BACK_LABEL": "Go Back",
"SEND_MESSAGE_LABEL": "Send Message",
"FORM_ERROR_MESSAGE": "Please fill all variables before sending",
"MEDIA_HEADER_LABEL": "{type} Header",
"OTP_CODE": "Enter 4-8 digit OTP",
"EXPIRY_MINUTES": "Enter expiry minutes",
"BUTTON_PARAMETERS": "Button Parameters",
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
}
-538
View File
@@ -1,538 +0,0 @@
[
{
"id": "1381151706284063",
"name": "event_invitation_static",
"status": "APPROVED",
"category": "MARKETING",
"language": "en",
"components": [
{
"text": "You're invited to {{event_name}} at {{location}}, Join us for an amazing experience!",
"type": "BODY",
"example": {
"body_text_named_params": [
{
"example": "F1",
"param_name": "event_name"
},
{
"example": "Dubai",
"param_name": "location"
}
]
}
},
{
"type": "BUTTONS",
"buttons": [
{
"url": "https://events.example.com/register",
"text": "Visit website",
"type": "URL"
},
{
"url": "https://maps.app.goo.gl/YoWAzRj1GDuxs6qz8",
"text": "Get Directions",
"type": "URL"
}
]
}
],
"sub_category": "CUSTOM",
"parameter_format": "NAMED"
},
{
"id": "767076159336759",
"name": "purchase_receipt",
"status": "APPROVED",
"category": "UTILITY",
"language": "en_US",
"components": [
{
"type": "HEADER",
"format": "DOCUMENT",
"example": {
"header_handle": [
"https://scontent.whatsapp.net/v/t61.29466-34/521502500_767076162670092_7371147273350347509_n.pdf?ccb=1-7&_nc_sid=8b1bef&_nc_ohc=x2s0Gbzdo68Q7kNvwFzj3k-&_nc_oc=AdkXq_pVHoQPmYQzalo1ND-wfvQT9GC_jJJphgVOxpqcTRktALRNit4t0tuNcX93kzU&_nc_zt=3&_nc_ht=scontent.whatsapp.net&edm=AH51TzQEAAAA&_nc_gid=OShKjgNv2PbNF9AIEuRgZA&oh=01_Q5Aa2AEmTve8tKHAhT_FIvARW7CFUeGf9d_1wiw3QUGu-FW_tQ&oe=68B1370B"
]
}
},
{
"text": "Thank you for using your {{1}} card at {{2}}. Your {{3}} is attached as a PDF.",
"type": "BODY",
"example": {
"body_text": [
[
"credit",
"CS Mutual",
"receipt"
]
]
}
}
],
"parameter_format": "POSITIONAL",
"library_template_name": "purchase_receipt_2"
},
{
"id": "729007633084301",
"name": "basic_otp",
"status": "APPROVED",
"category": "AUTHENTICATION",
"language": "en",
"components": [
{
"text": "*{{1}}* is your verification code. For your security, do not share this code.",
"type": "BODY",
"example": {
"body_text": [
[
"123456"
]
]
}
},
{
"type": "BUTTONS",
"buttons": [
{
"url": "https://www.whatsapp.com/otp/code/?otp_type=COPY_CODE&code=otp{{1}}",
"text": "Copy code",
"type": "URL",
"example": [
"https://www.whatsapp.com/otp/code/?otp_type=COPY_CODE&code=otp123456"
]
}
]
}
],
"parameter_format": "POSITIONAL",
"message_send_ttl_seconds": 600
},
{
"id": "1145229434109366",
"name": "secure_login_otp",
"status": "APPROVED",
"category": "AUTHENTICATION",
"language": "en",
"components": [
{
"text": "*{{1}}* is your verification code. For your security, do not share this code.",
"type": "BODY",
"example": {
"body_text": [
[
"123456"
]
]
}
},
{
"text": "This code expires in 10 minutes.",
"type": "FOOTER"
},
{
"type": "BUTTONS",
"buttons": [
{
"url": "https://www.whatsapp.com/otp/code/?otp_type=ZERO_TAP&cta_display_name=Autofill&package_name=com.chatwoot.app&signature_hash=12121212121&code_expiration_minutes=10&code=otp{{1}}",
"text": "Copy code",
"type": "URL",
"example": [
"https://www.whatsapp.com/otp/code/?otp_type=ZERO_TAP&cta_display_name=Autofill&package_name=com.chatwoot.app&signature_hash=12121212121&code_expiration_minutes=10&code=otp123456"
]
}
]
}
],
"parameter_format": "POSITIONAL",
"message_send_ttl_seconds": 600
},
{
"id": "1469258364071127",
"name": "discount_coupon",
"status": "APPROVED",
"category": "MARKETING",
"language": "en",
"components": [
{
"text": "🎉 Special offer for you! Get {{discount_percentage}}% off your next purchase. Use the code below at checkout",
"type": "BODY",
"example": {
"body_text_named_params": [
{
"example": "30",
"param_name": "discount_percentage"
}
]
}
},
{
"type": "BUTTONS",
"buttons": [
{
"text": "Copy offer code",
"type": "COPY_CODE",
"example": [
"SAVE1OFF"
]
}
]
}
],
"sub_category": "CUSTOM",
"parameter_format": "NAMED"
},
{
"id": "1075221534579807",
"name": "support_callback",
"status": "APPROVED",
"category": "UTILITY",
"language": "en",
"components": [
{
"text": "Hello {{name}}, our support team will call you regarding ticket # {{ticket_id}}.",
"type": "BODY",
"example": {
"body_text_named_params": [
{
"example": "muhsin",
"param_name": "name"
},
{
"example": "232323",
"param_name": "ticket_id"
}
]
}
},
{
"type": "BUTTONS",
"buttons": [
{
"text": "Call Support",
"type": "PHONE_NUMBER",
"phone_number": "+16506677566"
}
]
}
],
"sub_category": "CUSTOM",
"parameter_format": "NAMED"
},
{
"id": "1023596726651144",
"name": "training_video",
"status": "APPROVED",
"category": "MARKETING",
"language": "en",
"components": [
{
"type": "HEADER",
"format": "VIDEO",
"example": {
"header_handle": [
"https://scontent.whatsapp.net/v/t61.29466-34/521582686_1023596729984477_1872358575355618432_n.mp4?ccb=1-7&_nc_sid=8b1bef&_nc_ohc=MPOPgVrub7QQ7kNvwGMm61X&_nc_oc=AdnrE_z6RpqXyeEwVwrhiBQlpBy8DrqDD2QhPPQJaGj-97a93gS5jIVPzttJya__2tI&_nc_zt=28&_nc_ht=scontent.whatsapp.net&edm=AH51TzQEAAAA&_nc_gid=OShKjgNv2PbNF9AIEuRgZA&oh=01_Q5Aa2AFzaevej9gu9OdX2oy0aYNQqkkogb1mwGKDfTTi83fv0Q&oe=68B12B15"
]
}
},
{
"text": "Hi {{name}}, here's your training video. Please watch by{{date}}.",
"type": "BODY",
"example": {
"body_text_named_params": [
{
"example": "john",
"param_name": "name"
},
{
"example": "July 31",
"param_name": "date"
}
]
}
}
],
"sub_category": "CUSTOM",
"parameter_format": "NAMED"
},
{
"id": "1106685194739985",
"name": "order_confirmation",
"status": "APPROVED",
"category": "MARKETING",
"language": "en",
"components": [
{
"type": "HEADER",
"format": "IMAGE",
"example": {
"header_handle": [
"https://scontent.whatsapp.net/v/t61.29466-34/518466505_1106685198073318_3569250580697484416_n.jpg?ccb=1-7&_nc_sid=8b1bef&_nc_ohc=pzLMH8hPY0EQ7kNvwGidwBU&_nc_oc=Adn_w6yCXTBXnTtrYwxz7REXJFo_Oyy5OBtOMTFvc9_gNurQhQIiBvc1_xernpemAzw&_nc_zt=3&_nc_ht=scontent.whatsapp.net&edm=AH51TzQEAAAA&_nc_gid=OShKjgNv2PbNF9AIEuRgZA&oh=01_Q5Aa2AEHx0zLxNWnsUOwq5pcl8lyklT3fahHJXPw0ey25AfP3g&oe=68B13A47"
]
}
},
{
"text": "Hi your order {{1}} is confirmed. Please wait for further updates",
"type": "BODY",
"example": {
"body_text": [
[
"blue canvas shoes"
]
]
}
}
],
"sub_category": "CUSTOM",
"parameter_format": "POSITIONAL"
},
{
"id": "1242180011253003",
"name": "product_launch",
"status": "APPROVED",
"category": "MARKETING",
"language": "en",
"components": [
{
"type": "HEADER",
"format": "IMAGE",
"example": {
"header_handle": [
"https://scontent.whatsapp.net/v/t61.29466-34/510408113_1242180014586336_1564209110680717071_n.jpg?ccb=1-7&_nc_sid=8b1bef&_nc_ohc=5XTMSTbB6OsQ7kNvwHmy1uY&_nc_oc=AdlAssE60GFSe_C27DSuF16i8N7XbG5V68WxDwiZZCK2zQbouc0ItRm3vAHHD6vybC8&_nc_zt=3&_nc_ht=scontent.whatsapp.net&edm=AH51TzQEAAAA&_nc_gid=OShKjgNv2PbNF9AIEuRgZA&oh=01_Q5Aa2AFxHLQFYq2X4WlzHDCQ-Jb-bpde--W4v1cDXwxwwgY_yw&oe=68B1315C"
]
}
},
{
"text": "New arrival! Our stunning coat now available in {{color}} color.",
"type": "BODY",
"example": {
"body_text_named_params": [
{
"example": "blue",
"param_name": "color"
}
]
}
},
{
"text": "Free shipping on orders over $100. Limited time offer.",
"type": "FOOTER"
}
],
"sub_category": "CUSTOM",
"parameter_format": "NAMED"
},
{
"id": "1449876326175680",
"name": "technician_visit",
"status": "APPROVED",
"category": "UTILITY",
"language": "en_US",
"components": [
{
"text": "Technician visit",
"type": "HEADER",
"format": "TEXT"
},
{
"text": "Hi {{1}}, we're scheduling a technician visit to {{2}} on {{3}} between {{4}} and {{5}}. Please confirm if this time slot works for you.",
"type": "BODY",
"example": {
"body_text": [
[
"John",
"123 Maple St",
"2025-12-31",
"10:00 AM",
"2:00 PM"
]
]
}
},
{
"type": "BUTTONS",
"buttons": [
{
"text": "Confirm",
"type": "QUICK_REPLY"
},
{
"text": "Reschedule",
"type": "QUICK_REPLY"
}
]
}
],
"parameter_format": "POSITIONAL",
"library_template_name": "appointment_scheduling_address"
},
{
"id": "997298832221901",
"name": "greet",
"status": "APPROVED",
"category": "MARKETING",
"language": "en",
"components": [
{
"text": "Hey {{customer_name}} how may I help you?",
"type": "BODY",
"example": {
"body_text_named_params": [
{
"example": "John",
"param_name": "customer_name"
}
]
}
}
],
"sub_category": "CUSTOM",
"parameter_format": "NAMED"
},
{
"id": "632315222954611",
"name": "hello_world",
"status": "APPROVED",
"category": "UTILITY",
"language": "en_US",
"components": [
{
"text": "Hello World",
"type": "HEADER",
"format": "TEXT"
},
{
"text": "Welcome and congratulations!! This message demonstrates your ability to send a WhatsApp message notification from the Cloud API, hosted by Meta. Thank you for taking the time to test with us.",
"type": "BODY"
},
{
"text": "WhatsApp Business Platform sample message",
"type": "FOOTER"
}
],
"parameter_format": "POSITIONAL"
},
{
"id": "1005921651322035",
"name": "otp_verification",
"status": "APPROVED",
"category": "AUTHENTICATION",
"language": "en_US",
"components": [
{
"text": "Use code *{{1}}* to verify your transaction of {{2}}.",
"type": "BODY",
"example": {
"body_text": [
[
"123456",
"$12.34"
]
]
}
},
{
"type": "BUTTONS",
"buttons": [
{
"url": "https://www.whatsapp.com/otp/code/?otp_type=COPY_CODE&code=otp{{1}}",
"text": "Copy code",
"type": "URL",
"example": [
"https://www.whatsapp.com/otp/code/?otp_type=COPY_CODE&code=otp123456"
]
}
]
}
],
"parameter_format": "POSITIONAL",
"library_template_name": "verify_transaction_2",
"message_send_ttl_seconds": 600
},
{
"id": "787864066907971",
"name": "feedback_request",
"status": "APPROVED",
"category": "MARKETING",
"language": "en",
"components": [
{
"text": "Hey {{name}}, how was your experience with Puma? We'd love your feedback!",
"type": "BODY",
"example": {
"body_text_named_params": [
{
"example": "muhsin",
"param_name": "name"
}
]
}
},
{
"type": "BUTTONS",
"buttons": [
{
"url": "https://feedback.example.com/survey",
"text": "Leave Feedback",
"type": "URL"
}
]
}
],
"sub_category": "CUSTOM",
"parameter_format": "NAMED"
},
{
"id": "1938057163677205",
"name": "address_update",
"status": "APPROVED",
"category": "UTILITY",
"language": "en_US",
"components": [
{
"text": "Address update",
"type": "HEADER",
"format": "TEXT"
},
{
"text": "Hi {{1}}, your delivery address has been successfully updated to {{2}}. Contact {{3}} for any inquiries.",
"type": "BODY",
"example": {
"body_text": [
[
"John",
"123 Main St",
"support@telco.com"
]
]
}
}
],
"parameter_format": "POSITIONAL",
"library_template_name": "address_update"
},
{
"id": "1644094842949394",
"name": "delivery_confirmation",
"status": "APPROVED",
"category": "UTILITY",
"language": "en_US",
"components": [
{
"text": "{{1}}, your order was successfully delivered on {{2}}.\n\nThank you for your purchase.\n",
"type": "BODY",
"example": {
"body_text": [
[
"John",
"Jan 1, 2024"
]
]
}
}
],
"parameter_format": "POSITIONAL",
"library_template_name": "delivery_confirmation_4"
}
]
-351
View File
@@ -1,351 +0,0 @@
# WhatsApp Template Testing Cases
This document contains test cases for WhatsApp template functionality in Chatwoot, with screenshots showing expected behavior in both Chatwoot UI and WhatsApp delivery.
### hello_world
**Template Details:**
- **Name**: `hello_world`
- **Category**: UTILITY
- **Type**: Text Header + Body + Footer
- **Components**:
- Header: "Hello World" (TEXT)
- Body: "Welcome and congratulations!! This message demonstrates your ability to send a WhatsApp message notification from the Cloud API, hosted by Meta. Thank you for taking the time to test with us."
- Footer: "WhatsApp Business Platform sample message"
**Chatwoot UI**
![Chatwoot Template Configuration](templates/hello_world_chatwoot.png)
**WhatsApp Delivery**
![WhatsApp Delivered Message](templates/hello_world_whatsapp.png)
### greet
**Template Details:**
- **Name**: `greet`
- **Category**: MARKETING
- **Type**: Text Only (Body with Named Parameter)
- **Components**:
- Body: "Hey {{customer_name}} how may I help you?"
- **Parameters**:
- `customer_name` (NAMED parameter format)
**Chatwoot UI**
![Chatwoot Template Configuration](templates/greet_chatwoot.png)
**WhatsApp Delivery**
![WhatsApp Delivered Message](templates/greet_whatsapp.png)
### delivery_confirmation
**Template Details:**
- **Name**: `delivery_confirmation`
- **Category**: UTILITY
- **Type**: Text Only (Body with Positional Parameters)
- **Components**:
- Body: "{{1}}, your order was successfully delivered on {{2}}.\n\nThank you for your purchase.\n"
- **Parameters**:
- `{{1}}` - Customer name (POSITIONAL)
- `{{2}}` - Delivery date (POSITIONAL)
**Chatwoot UI**
![Chatwoot Template Configuration](templates/delivery_confirmation_chatwoot.png)
**WhatsApp Delivery**
![WhatsApp Delivered Message](templates/delivery_confirmation_whatsapp.png)
### address_update
**Template Details:**
- **Name**: `address_update`
- **Category**: UTILITY
- **Type**: Text Header + Body (Positional Parameters)
- **Components**:
- Header: "Address update" (TEXT)
- Body: "Hi {{1}}, your delivery address has been successfully updated to {{2}}. Contact {{3}} for any inquiries."
- **Parameters**:
- `{{1}}` - Customer name (POSITIONAL)
- `{{2}}` - New address (POSITIONAL)
- `{{3}}` - Contact info (POSITIONAL)
#### Chatwoot UI
![Chatwoot Template Configuration](templates/address_update_chatwoot.png)
#### WhatsApp Delivery
![WhatsApp Delivered Message](templates/address_update_whatsapp.png)
### order_confirmation
**Template Details:**
- **Name**: `order_confirmation`
- **Category**: MARKETING
- **Type**: Image Header + Body (Positional Parameters)
- **Components**:
- Header: IMAGE format
- Body: "Hi your order {{1}} is confirmed. Please wait for further updates"
- **Parameters**:
- Media URL for image header
- `{{1}}` - Order details (POSITIONAL)
**Chatwoot UI**
![Chatwoot Template Configuration](templates/order_confirmation_chatwoot.png)
**WhatsApp Delivery**
![WhatsApp Delivered Message](templates/order_confirmation_whatsapp.png)
### product_launch
**Template Details:**
- **Name**: `product_launch`
- **Category**: MARKETING
- **Type**: Image Header + Body + Footer (Named Parameters)
- **Components**:
- Header: IMAGE format
- Body: "New arrival! Our stunning coat now available in {{color}} color."
- Footer: "Free shipping on orders over $100. Limited time offer."
- **Parameters**:
- Media URL for image header
- `color` - Product color (NAMED)
**Chatwoot UI**
![Chatwoot Template Configuration](templates/product_launch_chatwoot.png)
**WhatsApp Delivery**
![WhatsApp Delivered Message](templates/product_launch_whatsapp.png)
### training_video
**Template Details:**
- **Name**: `training_video`
- **Category**: MARKETING
- **Type**: Video Header + Body (Named Parameters)
- **Components**:
- Header: VIDEO format
- Body: "Hi {{name}}, here's your training video. Please watch by{{date}}."
- **Parameters**:
- Media URL for video header
- `name` - Employee name (NAMED)
- `date` - Due date (NAMED)
**Chatwoot UI**
![Chatwoot Template Configuration](templates/training_video_chatwoot.png)
**WhatsApp Delivery**
![WhatsApp Delivered Message](templates/training_video_whatsapp.png)
### purchase_receipt
**Template Details:**
- **Name**: `purchase_receipt`
- **Category**: UTILITY
- **Type**: Document Header + Body (Positional Parameters)
- **Components**:
- Header: DOCUMENT format
- Body: "Thank you for using your {{1}} card at {{2}}. Your {{3}} is attached as a PDF."
- **Parameters**:
- Media URL for document header
- `{{1}}` - Card type (POSITIONAL)
- `{{2}}` - Merchant name (POSITIONAL)
- `{{3}}` - Document type (POSITIONAL)
**Chatwoot UI**
![Chatwoot Template Configuration](templates/purchase_receipt_chatwoot.png)
**WhatsApp Delivery**
![WhatsApp Delivered Message](templates/purchase_receipt_whatsapp.png)
### event_invitation_static
**Template Details:**
- **Name**: `event_invitation_static`
- **Category**: MARKETING
- **Type**: Body + Static URL Buttons (Named Parameters)
- **Components**:
- Body: "You're invited to {{event_name}} at {{location}}, Join us for an amazing experience!"
- Buttons: 2 URL buttons ("Visit website", "Get Directions") with static URLs
- **Parameters**:
- `event_name` - Event name (NAMED)
- `location` - Event location (NAMED)
- **Button URLs**:
- Visit website: `https://events.example.com/register` (static)
- Get Directions: `https://maps.app.goo.gl/YoWAzRj1GDuxs6qz8` (static)
**Chatwoot UI**
![Chatwoot Template Configuration](templates/event_invitation_static_chatwoot.png)
**WhatsApp Delivery**
![WhatsApp Delivered Message](templates/event_invitation_static_whatsapp.png)
### feedback_request
**Template Details:**
- **Name**: `feedback_request`
- **Category**: MARKETING
- **Type**: Body + URL Button (Named Parameters)
- **Components**:
- Body: "Hey {{name}}, how was your experience with Puma? We'd love your feedback!"
- Buttons: 1 URL button ("Leave Feedback") with static URL
- **Parameters**:
- `name` - Customer name (NAMED)
- **Button URL**:
- Leave Feedback: `https://feedback.example.com/survey` (static)
**Chatwoot UI**
![Chatwoot Template Configuration](templates/feedback_request_chatwoot.png)
**WhatsApp Delivery**
![WhatsApp Delivered Message](templates/feedback_request_whatsapp.png)
### support_callback
**Template Details:**
- **Name**: `support_callback`
- **Category**: UTILITY
- **Type**: Body + Phone Button (Named Parameters)
- **Components**:
- Body: "Hello {{name}}, our support team will call you regarding ticket # {{ticket_id}}."
- Buttons: 1 PHONE_NUMBER button ("Call Support")
- **Parameters**:
- `name` - Customer name (NAMED)
- `ticket_id` - Support ticket ID (NAMED)
**Chatwoot UI**
![Chatwoot Template Configuration](templates/support_callback_chatwoot.png)
**WhatsApp Delivery**
![WhatsApp Delivered Message](templates/support_callback_whatsapp.png)
### discount_coupon
**Template Details:**
- **Name**: `discount_coupon`
- **Category**: MARKETING
- **Type**: Body + Copy Code Button (Named Parameters)
- **Components**:
- Body: "🎉 Special offer for you! Get {{discount_percentage}}% off your next purchase. Use the code below at checkout"
- Buttons: 1 COPY_CODE button ("Copy offer code")
- **Parameters**:
- `discount_percentage` - Discount amount (NAMED)
- Coupon code for button
**Chatwoot UI**
![Chatwoot Template Configuration](templates/discount_coupon_chatwoot.png)
**WhatsApp Delivery**
![WhatsApp Delivered Message](templates/discount_coupon_whatsapp.png)
### technician_visit
**Template Details:**
- **Name**: `technician_visit`
- **Category**: UTILITY
- **Type**: Text Header + Body + Quick Reply Buttons (Positional Parameters)
- **Components**:
- Header: "Technician visit" (TEXT)
- Body: "Hi {{1}}, we're scheduling a technician visit to {{2}} on {{3}} between {{4}} and {{5}}. Please confirm if this time slot works for you."
- Buttons: 2 QUICK_REPLY buttons ("Confirm", "Reschedule")
- **Parameters**:
- `{{1}}` - Customer name (POSITIONAL)
- `{{2}}` - Address (POSITIONAL)
- `{{3}}` - Date (POSITIONAL)
- `{{4}}` - Start time (POSITIONAL)
- `{{5}}` - End time (POSITIONAL)
**Chatwoot UI**
![Chatwoot Template Configuration](templates/technician_visit_chatwoot.png)
**WhatsApp Delivery**
![WhatsApp Delivered Message](templates/technician_visit_whatsapp.png)
### basic_otp
**Template Details:**
- **Name**: `basic_otp`
- **Category**: AUTHENTICATION
- **Type**: Body + URL Button (Positional Parameters)
- **Components**:
- Body: "*{{1}}* is your verification code. For your security, do not share this code."
- Buttons: 1 URL button with OTP code ("Copy code")
- **Parameters**:
- `{{1}}` - OTP code (POSITIONAL)
- **Special Features**: Auto-populated button parameter with OTP code
**Chatwoot UI**
![Chatwoot Template Configuration](templates/basic_otp_chatwoot.png)
**WhatsApp Delivery**
![WhatsApp Delivered Message](templates/basic_otp_whatsapp.png)
### otp_verification
**Template Details:**
- **Name**: `otp_verification`
- **Category**: AUTHENTICATION
- **Type**: Body + URL Button (Positional Parameters)
- **Components**:
- Body: "Use code *{{1}}* to verify your transaction of {{2}}."
- Buttons: 1 URL button with OTP code ("Copy code")
- **Parameters**:
- `{{1}}` - OTP code (POSITIONAL)
- `{{2}}` - Transaction amount (POSITIONAL)
- **Special Features**: Auto-populated button parameter with OTP code
**Chatwoot UI**
![Chatwoot Template Configuration](templates/otp_verification_chatwoot.png)
**WhatsApp Delivery**
![WhatsApp Delivered Message](templates/otp_verification_whatsapp.png)
### secure_login_otp
**Template Details:**
- **Name**: `secure_login_otp`
- **Category**: AUTHENTICATION
- **Type**: Body + Footer + URL Button (Positional Parameters)
- **Components**:
- Body: "*{{1}}* is your verification code. For your security, do not share this code."
- Footer: "This code expires in 10 minutes."
- Buttons: 1 URL button with Zero-tap auto-fill ("Copy code")
- **Parameters**:
- `{{1}}` - OTP code (POSITIONAL)
- **Special Features**: Zero-tap auto-fill functionality, Auto-populated button parameter
**Chatwoot UI**
![Chatwoot Template Configuration](templates/secure_login_otp_chatwoot.png)
#### WhatsApp Delivery
![WhatsApp Delivered Message](templates/secure_login_otp_whatsapp.png)
-365
View File
@@ -1,365 +0,0 @@
# WhatsApp Templates in Chatwoot
Chatwoot supports WhatsApp message templates for outbound messaging via the WhatsApp Business Platform.
📘 **Reference**: Meta Docs [Message Templates](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates)
## Overview
WhatsApp Business API requires pre-approved message templates for initiating conversations with customers. Chatwoot provides support for creating and sending various types of templates while maintaining compliance with WhatsApp's template policies.
## ✅ What is Supported
### Template Categories
- **UTILITY** - Transactional messages (order confirmations, receipts, etc.)
- **MARKETING** - Promotional content and offers
- **AUTHENTICATION** - OTP codes and verification messages with automatic button parameter population
- **SHIPPING_UPDATE** - Package and delivery status updates
- **TICKET_UPDATE** - Support ticket notifications
- **ISSUE_RESOLUTION** - Customer service follow-ups
### Template Components
#### Headers
- **TEXT** - Plain text headers with variable placeholders ({{1}}, {{2}}, etc.)
- **IMAGE** - Image headers with media URL parameters (JPEG, PNG)
- **VIDEO** - Video headers with media URL parameters (MP4, 3GPP)
- **DOCUMENT** - Document headers with file URL parameters (PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX, TXT)
- **None** - Templates without headers
#### Body (Required)
- **TEXT** - Always required component that supports:
- Static text content
- Variable placeholders ({{1}}, {{2}}, etc.)
- Special authentication parameters (OTP codes, expiry times)
- Rich text formatting preservation
#### Footer (Optional)
- **TEXT** - Plain text footers with variable support
#### Buttons (Optional)
- **URL** - Call-to-action buttons with dynamic URLs and variable parameters
- **PHONE_NUMBER** - Click-to-call buttons
- **COPY_CODE** - Coupon/discount code buttons (max 15 characters)
- **QUICK_REPLY** - Interactive reply buttons for user responses
### Special Template Types
#### Authentication Templates
- **OTP Templates**: Automatic OTP code and expiry time parameter handling
- **Button Auto-population**: URL buttons automatically populated with OTP codes
- **Enhanced Validation**: OTP codes (4-8 digits), expiry times validation
- **Structured Parameters**: Organized body and button parameter processing
#### Media Templates
- **Image Templates**: Full support for JPEG, PNG formats with URL validation
- **Video Templates**: Full support for MP4, 3GPP formats with URL validation
- **Document Templates**: **NEW** - Full support for PDF, Office documents, and text files
- **URL Validation**: Comprehensive validation, sanitization, and accessibility checks
- **Media Parameter Building**: Proper WhatsApp API format generation
## 🔄 Supported Template Combinations
### Basic Templates
1. **Text Only**: Body component only
2. **Text + Footer**: Body with footer text
3. **Text Header + Body**: Text header with body content
4. **Text Header + Body + Footer**: Full text template with all components
### Media Templates
5. **Image Header + Body**: Image with descriptive text
6. **Image Header + Body + Footer**: Image template with footer
7. **Video Header + Body**: Video with descriptive text
8. **Document Header + Body**: **NEW** - Document with description (PDFs, Office docs, etc.)
9. **Media + Buttons**: Any media header with action buttons
### Button Templates
10. **Body + URL Button**: Text with call-to-action and dynamic URLs
11. **Body + Phone Button**: Text with click-to-call
12. **Body + Copy Code**: Text with coupon code validation
13. **Body + Quick Reply Button**: Text with interactive reply buttons
14. **Authentication + URL Button**: OTP templates with auto-populated button parameters
### Authentication Templates
15. **OTP Template**: Body with OTP code and expiry with enhanced processing
16. **OTP + Footer**: OTP template with footer text
17. **OTP + URL Buttons**: **NEW** - OTP template with auto-populated action buttons
## ❌ What is Not Supported
### Interactive Components
- **LIST** templates with selectable options
- **PRODUCT** templates with catalog integration
- **CATALOG** templates for product browsing
- **Multi-select** components
### Location Components
- **LOCATION** headers with map coordinates
- **Address** parameters with latitude/longitude
- **Location-based** templates
### Advanced Features
- **Rich text formatting** in template creation (preserved in sending)
- **Carousel** templates with multiple cards
- **Form** components for data collection
- **Payment** integration templates
- **Flow** templates with conditional logic
### Limitations
- **Button limit**: Maximum 3 buttons per template
- **Variable limit**: Maximum 10 variables per component
- **Character limits**: Per WhatsApp Business API restrictions
- **Media size limits**: Documents < 100MB, Images < 5MB, Videos < 16MB
## 🚀 Using Templates in Chatwoot
### Template Creation
1. Create templates through WhatsApp Business Manager
2. Ensure templates are in **approved** status
3. Templates will automatically/manually sync to Chatwoot
4. Unsupported template types are automatically filtered out
### Sending Templates
#### Legacy Template Interface (Current)
1. **Select Template**: Choose from approved templates in the picker
2. **Fill Parameters**:
- **Media URLs**: For image/video/document headers (must be publicly accessible)
- **Body Variables**: Text values for {{1}}, {{2}}, etc. placeholders
- **Button Parameters**: Dynamic values for URL buttons and copy codes
- **Authentication Values**: OTP codes with automatic validation
### Parameter Validation & Processing
#### Frontend Processing
- **Variable Detection**: Automatic parsing of {{}} placeholders
- **Parameter Organization**: Structured grouping by component type
- **Validation Rules**: Real-time validation with user feedback
- **Special Handling**: Authentication template auto-population
#### Backend Processing
- **Enhanced Template Processing**: Handles structured parameter format
- **Media Parameter Building**: Generates proper WhatsApp API media objects
- **Authentication Support**: Auto-populates button parameters with OTP values
- **URL Validation**: Comprehensive accessibility and format checking
## 🔧 Technical Implementation
### Frontend Architecture
#### Components
- **TemplatesPicker.vue**: Template selection with filtering for supported types
- **TemplateParser.vue**: **Enhanced** - Parameter input with structured processing
- Media URL input for IMAGE/VIDEO/DOCUMENT headers
- Body parameter handling with authentication special cases
- Button parameter processing with auto-population
- Enhanced validation and error handling
#### TODO:
- Add support all the advanced template types for new Conversation templates
- Add support for all the advanced template types for WhatsApp Campaigns
### Backend Architecture
#### Core Service
- **TemplateProcessorService**: Main processing engine with multiple pathways:
- **Enhanced Processing**: For structured parameters (header/body/buttons)
- **Legacy Processing**: For simple text-only templates
- **Authentication Processing**: Special handling for OTP templates
- **Media Processing**: Dedicated handling for IMAGE/VIDEO/DOCUMENT templates
#### Processing Flow
```ruby
# 1. Parameter Routing
if structured_params?(header/body/buttons)
process_enhanced_template_params()
else
# Legacy processing for simple templates
process_legacy_template_params()
end
2. Component Building
- Header: Media parameter generation
- Body: Text parameter processing
- Buttons: URL/copy_code parameter handling
3. WhatsApp API Formatting
Convert to proper WhatsApp Cloud API structure
```
#### Media Parameter Handling
- **URL Validation**: Scheme, accessibility, format checking
- **Media Type Detection**: Automatic format detection and validation
- **Parameter Building**: Generates proper WhatsApp media objects
- **Error Handling**: Comprehensive validation with helpful error messages
### API Integration
- **WhatsApp Cloud API**: Primary integration for template sending
- **Template Sync**: Automatic synchronization of approved templates with filtering
- **Parameter Formatting**: Converts Chatwoot structured parameters to WhatsApp format
- **Error Handling**: Validates parameters and provides detailed error feedback
## 📋 Best Practices
### Template Design
- Keep message content **clear and concise**
- Use **meaningful variable names** for better organization
- Test templates thoroughly before approval
- Follow WhatsApp's **template policies** and guidelines
### Parameter Management
- **Media URLs**: Use publicly accessible URLs (avoid temporary/auth-required URLs)
-`https://your-domain.com/files/document.pdf`
-`https://scontent.whatsapp.net/...` (temporary WhatsApp URLs)
- **Authentication Templates**: Let system auto-populate button parameters
- **Validation**: Always validate parameters before sending
- **Error Handling**: Implement proper error handling for failed parameters
### Media Best Practices
- **Documents**: Use direct download URLs, ensure < 100MB file size
- **Images**: Optimize for mobile viewing, ensure < 5MB file size
- **Videos**: Keep under 16MB, use MP4 format for best compatibility
- **Accessibility**: Ensure all media URLs are publicly accessible without authentication
### Compliance
- Ensure all templates are **properly approved** before use
- Follow **opt-in requirements** for marketing templates
- Respect **rate limits** and sending windows
- Monitor **template quality ratings** and compliance metrics
## 🆘 Troubleshooting
### Common Issues
#### Template Issues
- **Template not appearing**: Check approval status and supported format
- **Interactive templates missing**: LIST/PRODUCT/CATALOG templates are not supported
- **Location templates missing**: LOCATION templates are not supported
#### Parameter Issues
- **Parameter validation errors**: Verify required fields are filled
- **Media loading failures**: Ensure URLs are publicly accessible and valid format
- **Button parameters empty**: Check auto-population for authentication templates
#### Sending Failures
- **Format mismatch errors**: Verify template structure matches expected format
- **Media upload errors (131053)**: Check media URL accessibility and file size
- **Missing parameter errors**: Ensure all required template variables are filled
### Error Messages & Solutions
#### Media Errors
- `Media upload error (131053)`:
- **Cause**: URL not accessible, file too large, or unsupported format
- **Solution**: Use publicly accessible URLs, check file size limits
#### Parameter Errors
- `Required parameter is missing`:
- **Cause**: Template variables not filled or button parameters missing
- **Solution**: Fill all required fields, check authentication auto-population
#### Template Errors
- `Template not found`: Template may not be approved or synced
- `Invalid parameter format`: Check variable formatting and requirements
- `OTP validation failed`: Ensure OTP is 4-8 digits numeric only
### Debugging Support
- Check WhatsApp Business Manager for template approval status
- Verify template compliance with supported component types
- Review parameter structure and validation requirements
- Check media URL accessibility independently
- Monitor Rails logs for detailed error information
## 📈 Future Roadmap & TODOs
### Future Enhancements
While Chatwoot currently provides comprehensive core template functionality, potential future improvements include:
- **Rich text formatting** support in template creation
- **Enhanced media validation** with format conversion
- **Template performance analytics** and usage metrics
- **Advanced parameter management** with preset values
- **Custom template validation rules** and business logic
- **Template testing environment** for development
- **Media asset management** with CDN integration
### Integration Improvements
- **Webhook support** for template status changes
- **Advanced error handling** with retry mechanisms
- **Template versioning** and rollback capabilities
- **Multi-language template** management
- **Template approval workflow** integration
## ⚠️ Known Limitations
### Dynamic URL Button Issues
Currently, there are known issues with dynamic URL buttons in certain templates due to WhatsApp Business Manager generating malformed URL structures with mixed parameter formats. **Workaround**: Use static URLs in your button templates instead of dynamic parameters for reliable functionality.
### QA Checklist
**Complete Template Coverage: 16 Templates covering all 17 Supported Combinations**
| Template Name | Category | Header | Buttons | Template Type | Coverage |
| --- | --- | --- | --- | --- | --- |
| greet | MARKETING | None | None | Text Only | ✅ |
| delivery_confirmation | UTILITY | None | None | Text Only | ✅ |
| hello_world | UTILITY | TEXT | None | Text Header + Body + Footer | ✅ |
| address_update | UTILITY | TEXT | None | Text Header + Body | ✅ |
| order_confirmation | MARKETING | IMAGE | None | Image Header + Body | ✅ |
| product_launch | MARKETING | IMAGE | None | Image Header + Body + Footer | ✅ |
| training_video | MARKETING | VIDEO | None | Video Header + Body | ✅ |
| purchase_receipt | UTILITY | DOCUMENT | None | Document Header + Body | ✅ |
| event_invitation_static | MARKETING | None | URL, URL | Body + Static URL Buttons | ✅ |
| feedback_request | MARKETING | None | URL | Body + URL Button | ✅ |
| support_callback | UTILITY | None | PHONE_NUMBER | Body + Phone Button | ✅ |
| discount_coupon | MARKETING | None | COPY_CODE | Body + Copy Code | ✅ |
| technician_visit | UTILITY | TEXT | QUICK_REPLY, QUICK_REPLY | Body + Quick Reply Button | ✅ |
| basic_otp | AUTHENTICATION | None | URL (Copy Code) | Basic OTP Template | ✅ |
| otp_verification | AUTHENTICATION | None | URL (Copy Code) | Authentication + URL Button | ✅ |
| secure_login_otp | AUTHENTICATION | None | URL (Zero-tap) | OTP + Footer + Zero-tap | ✅ |
**Summary:**
- **Total Templates**: 16
- **Template Combinations Covered**: 17/17 (100%)
- **Authentication Methods**: Copy Code, Zero-tap Auto-fill
- **Media Types**: TEXT, IMAGE, VIDEO, DOCUMENT
- **Button Types**: URL, PHONE_NUMBER, COPY_CODE, QUICK_REPLY
- **Parameter Formats**: NAMED, POSITIONAL