PRIORITY: High
CATEGORY: Bug Report - Design Studio UI
AFFECTED COMPONENT: Bot Design Studio / Intent Actions Editor
DESCRIPTION:
We are experiencing an issue where the Tiledesk Design Studio UI does not display the headers and body configuration fields for webrequestv2 actions when a bot is imported via the API endpoint /v3/{PROJECT_ID}/bots/importjson/{BOT_ID}.
STEPS TO REPRODUCE:
- Create a bot using POST /v3/{PROJECT_ID}/faq_kb
- Import bot JSON with intents containing webrequestv2 actions using POST /v3/{PROJECT_ID}/bots/importjson/{BOT_ID}
- The JSON payload includes:
- headers: object with Content-Type and Authorization
- trueHeaders: array of key-value pairs
- bodyType: “formData” or “json”
- formData: array of form fields
- jsonBody: string with JSON body
- trueJsonBody: object with JSON body
- Open the bot in Tiledesk Design Studio
- Click on the imported intent (e.g., “Hi”)
- Click on a webrequestv2 action block (e.g., “Get Auth Token” or “Send Push API”)
EXPECTED BEHAVIOR:
The Design Studio UI should display:
- Headers section showing all configured headers (Content-Type, Authorization, etc.)
- Body/Form Data section showing configured form fields or JSON body
- All fields should be editable in the UI
ACTUAL BEHAVIOR:
The Design Studio UI does NOT display:
- Headers section is empty/missing
- Body/Form Data section is empty/missing
- Fields appear as if they were never configured
VERIFICATION:
We have verified via direct API calls (GET /v3/{PROJECT_ID}/faq/{INTENT_ID}) that the data IS correctly saved in the database with all headers, formData, jsonBody, trueHeaders, and trueJsonBody fields present.
EXAMPLE DATA STRUCTURE SAVED (confirmed via API):
{
"_tdActionType": "webrequestv2",
"url": "https://auth.aclwhatsapp.com/realms/ipmessaging/protocol/openid-connect/token",
"method": "POST",
"headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"trueHeaders": [
{
"key": "Content-Type",
"value": "application/x-www-form-urlencoded"
}
],
"bodyType": "formData",
"formData": [
{
"key": "grant_type",
"value": "password"
},
{
"key": "client_id",
"value": "ipmessaging-client"
},
{
"key": "username",
"value": "${auth_username}"
},
{
"key": "password",
"value": "${auth_password}"
}
]
}
IMPACT:
- High: This prevents our clients from viewing and editing API configurations in the UI
- Users cannot modify headers or body parameters without re-creating actions manually
- This blocks our use case of automated bot provisioning for multiple enterprises
- We need clients to be able to edit these fields in the UI for their specific configurations
TESTED WITH:
- Tiledesk API v3
- Multiple field formats: headers + trueHeaders, formData + bodyRaw, jsonBody + trueJsonBody
- Endpoint: POST /v3/{PROJECT_ID}/bots/importjson/{BOT_ID}
- Browser: Tested with cache cleared, hard refresh, incognito mode
WORKAROUND ATTEMPTED:
- Adding both “headers” object and “trueHeaders” array
- Adding both “formData” array and “bodyRaw” string
- Adding both “jsonBody” string and “trueJsonBody” object
- Using PUT /v3/{PROJECT_ID}/faq/{INTENT_ID} to force UI refresh
- All workarounds failed - UI still doesn’t display the fields
REQUEST:
Please investigate why the Design Studio UI is not rendering these fields and provide either:
- A fix to display API-imported webrequestv2 configurations in the UI, OR
- The correct data structure format that the UI requires for proper rendering, OR
- An alternative API endpoint or method to import bots with full UI compatibility
ADDITIONAL INFORMATION:
- We have confirmed the bot functions correctly when tested (APIs execute properly)
- The issue is purely a UI rendering problem
- Data is correctly stored in the database
- This affects our enterprise deployment automation
Thank you for your assistance.
-
Screenshot:–
========================================
2–BotTemplate.json
{
“webhook_enabled”: false,
“language”: “en”,
“name”: “ENTERPRISE_NAME_BOT”,
“type”: “tilebot”,
“subtype”: “chatbot”,
“attributes”: {
"variables": {
"auth_username": "xxxx",
"auth_password": "xxxx",
"bearer_token": "",
"sender": "xxxx“,
"to": "919876543210",
"header_text": "HDFC Life Sanchay Plus",
"body_text": "Please click on the below button to share your address.",
"flow_id": "123456789",
"mode": "draft"
}
},
“intents”: [
{
"webhook_enabled": false,
"enabled": true,
"actions": \[
{
"\_tdActionTitle": "Get Auth Token",
"\_tdActionId": "token-api-action",
"\_tdActionType": "webrequestv2",
"url": "https://auth.aclwhatsapp.com/realms/ipmessaging/protocol/openid-connect/token",
"method": "POST",
"trueHeaders": \[
{
"key": "Content-Type",
"value": "application/x-www-form-urlencoded"
}
\],
"headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"bodyType": "formData",
"bodyRaw": "grant_type=password&client_id=ipmessaging-client&username=${auth_username}&password=${auth_password}",
"formData": \[
{
"key": "grant_type",
"value": "password"
},
{
"key": "client_id",
"value": "ipmessaging-client"
},
{
"key": "username",
"value": "${auth_username}"
},
{
"key": "password",
"value": "${auth_password}"
}
\],
"assignResultTo": "token_response",
"assignStatusTo": "token_status",
"assignErrorTo": "token_error",
"assignments": {
"bearer_token": "token_response.access_token"
},
"settings": {
"timeout": 20000
},
"attributes": {
"nextBlockAction": {
"\_tdActionTitle": "Send Push API",
"\_tdActionId": "push-api-action",
"\_tdActionType": "webrequestv2"
}
}
},
{
"\_tdActionTitle": "Send Push API",
"\_tdActionId": "push-api-action",
"\_tdActionType": "webrequestv2",
"url": "https://push.aclwhatsapp.com/pull-platform-receiver/wa/messages",
"method": "POST",
"trueHeaders": \[
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Authorization",
"value": "Bearer ${bearer_token}"
}
\],
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer ${bearer_token}"
},
"bodyType": "json",
"trueJsonBody": {
"messages": \[
{
"sender": "${sender}",
"to": "${to}",
"messageId": "",
"transactionId": "",
"channel": "wa",
"callbackDlrUrl": "",
"type": "interactive",
"interactive": {
"type": "flow",
"header": {
"type": "text",
"text": "${header_text}"
},
"body": {
"text": "${body_text}"
},
"action": {
"name": "flow",
"parameters": {
"flow_message_version": "3",
"flow_token": "1234",
"flow_action": "navigate",
"flow_id": "${flow_id}",
"flow_cta": "Address",
"mode": "${mode}",
"flow_action_payload": {
"screen": "First_Screen",
"data": {}
}
}
}
}
}
\],
"responseType": "json"
},
"jsonBody": "{\\"messages\\":\[{\\"sender\\":\\"${sender}\\",\\"to\\":\\"${to}\\",\\"messageId\\":\\"\\",\\"transactionId\\":\\"\\",\\"channel\\":\\"wa\\",\\"callbackDlrUrl\\":\\"\\",\\"type\\":\\"interactive\\",\\"interactive\\":{\\"type\\":\\"flow\\",\\"header\\":{\\"type\\":\\"text\\",\\"text\\":\\"${header_text}\\"},\\"body\\":{\\"text\\":\\"${body_text}\\"},\\"action\\":{\\"name\\":\\"flow\\",\\"parameters\\":{\\"flow_message_version\\":\\"3\\",\\"flow_token\\":\\"1234\\",\\"flow_action\\":\\"navigate\\",\\"flow_id\\":\\"${flow_id}\\",\\"flow_cta\\":\\"Address\\",\\"mode\\":\\"${mode}\\",\\"flow_action_payload\\":{\\"screen\\":\\"First_Screen\\",\\"data\\":{}}}}}}\],\\"responseType\\":\\"json\\"}",
"assignResultTo": "push_response",
"assignStatusTo": "push_status",
"assignErrorTo": "push_error",
"assignments": {},
"settings": {
"timeout": 20000
},
"attributes": {
"nextBlockAction": {
"\_tdActionTitle": "Success Message",
"\_tdActionId": "success-message",
"\_tdActionType": "reply"
}
}
},
{
"\_tdActionTitle": "Success Message",
"\_tdActionId": "success-message",
"\_tdActionType": "reply",
"attributes": {
"disableInputMessage": false,
"commands": \[
{
"type": "wait",
"time": 500
},
{
"type": "message",
"message": {
"type": "text",
"text": "✅ Your request has been processed successfully!",
"attributes": {
"attachment": {
"type": "template",
"buttons": \[\]
}
}
}
}
\]
},
"text": "✅ Your request has been processed successfully!"
}
\],
"intent_display_name": "Hi",
"intent_id": "hi-intent-with-apis",
"question": "hi\\nhello\\nhey",
"answer": "",
"language": "en",
"attributes": {
"position": {
"x": 100,
"y": 100
}
},
"agents_available": false
}
]
}
3–Script that i am running
const axios = require(“axios”);
const fs = require(“fs”);
//
Tiledesk Credentials
const EMAIL = “vedita.gupta@sinch.com”;
const PASSWORD = “Jaishreeram@123”;
//
Project Configuration - Dynamic from database
const NEW_PROJECT_NAME = “HDFC_Project”; // Enterprise-specific project name
const CREATE_NEW_PROJECT = true; // Set to true to create new project, false to use existing
//
Bot Configuration - These will be dynamic from database
const ENTERPRISE_BOT_NAME = “HDFC_Bot”; // This will come from DB - Enterprise name
const CREATE_NEW_BOT = true; // Always create new bot for each enterprise
//
Database Configuration - These values will come from your database
const DB_CONFIG = {
// Auth API credentials
auth_username: “xxxx”,
auth_password: “xxxx”,
// Push API configuration
sender: “916389390966”,
to: “919876543210”,
header_text: “HDFC Life Sanchay Plus”,
body_text: “Please click on the below button to share your address.”,
flow_id: “123456789”,
mode: “draft”
};
//
Bot Template JSON
const BOT_TEMPLATE_PATH = “./BotTemplate.json”;
async function run() {
try {
*// 1️⃣ LOGIN → TOKEN*
*console.log*("🔐 Logging in...");
const *loginRes* = *await axios.post*(
"https://api.tiledesk.com/v3/auth/signin",
{ *email*: *EMAIL*, *password*: *PASSWORD* }
);
const *TOKEN* = *loginRes.data.token*;
if (!TOKEN) *throw* new *Error*("Token not received");
*console.log*("✅ Token received");
let *PROJECT_ID*;
if (CREATE_NEW_PROJECT) {
*// 2️⃣ CREATE NEW PROJECT*
*console.log*("🏢 Creating new project...");
const *newProjectData* = {
*name*: *NEW_PROJECT_NAME*
};
const *createProjectRes* = *await axios.post*(
"https://api.tiledesk.com/v3/projects",
newProjectData,
{
*headers*: {
*Authorization*: *TOKEN*,
"Content-Type": "application/json"
}
}
);
PROJECT_ID = *createProjectRes.data.*\_id;
*console.log*("✅ New Project created!");
*console.log*(\` - Project Name: ${*createProjectRes.data.*name}\`);
*console.log*(\` - Project ID: ${PROJECT_ID}\`);
} else {
*// 2️⃣ GET EXISTING PROJECT → PROJECT ID*
*console.log*("📁 Fetching existing projects...");
const *projectsRes* = *await axios.get*(
"https://api.tiledesk.com/v3/projects",
{
*headers*: {
*Authorization*: *TOKEN*
}
}
);
*console.log*("Available projects:");
*projectsRes.data.forEach*(p => {
const *projectName* = *p.id_project?.name* || 'No name';
*console.log*(\` - ${projectName} (ID: ${*p.id_project?.*\_id || *p.*\_id})\`);
});
*// Find the most recent project with matching name (sort by creation date)*
const *matchingProjects* = *projectsRes.data.filter*(
*p* => *p.id_project?.name* === *NEW_PROJECT_NAME*
);
if (*matchingProjects.*length === 0) {
*throw* new *Error*(\`Project '${NEW_PROJECT_NAME}' not found\`);
}
*// Sort by createdAt descending to get the newest*
*matchingProjects.sort*((a, b) => {
const *dateA* = new *Date*(*a.id_project.createdAt* || 0);
const *dateB* = new *Date*(*b.id_project.createdAt* || 0);
*return* dateB - dateA;
});
const *project* = matchingProjects\[0\];
PROJECT_ID = *project.id_project.*\_id;
*console.log*(\`✅ Project ID: ${PROJECT_ID} (using most recent)\`);
}
*// 3️⃣ READ BOT TEMPLATE JSON*
*console.log*("📄 Reading bot template JSON from file...");
const *botTemplate* = *JSON.parse*(*fs.readFileSync*(*BOT_TEMPLATE_PATH*, "utf8"));
*// 4️⃣ CUSTOMIZE BOT WITH DYNAMIC VALUES*
*console.log*("🔧 Customizing bot with enterprise data...");
*// Set bot name from enterprise*
*botTemplate.*name = ENTERPRISE_BOT_NAME;
*// Update variables with database configuration*
*botTemplate.attributes.*variables = {
...*botTemplate.attributes.*variables,
auth_username: DB_CONFIG*.*auth_username,
auth_password: DB_CONFIG*.*auth_password,
sender: DB_CONFIG*.*sender,
to: DB_CONFIG*.*to,
header_text: DB_CONFIG*.*header_text,
body_text: DB_CONFIG*.*body_text,
flow_id: DB_CONFIG*.*flow_id,
mode: DB_CONFIG*.*mode
};
const *botData* = botTemplate; *// Use customized template as botData*
*console.log*(\`📊 Bot Configuration:\`);
*console.log*(\` - Bot Name: ${*botData.*name}\`);
*console.log*(\` - Type: ${*botData.*type}\`);
*console.log*(\` - Intents count: ${*botData.intents?.*length || 0}\`);
*console.log*(\` - Auth Username: ${DB_CONFIG*.*auth_username}\`);
*console.log*(\` - Sender: ${DB_CONFIG*.*sender}\`);
*console.log*(\` - Receiver: ${DB_CONFIG*.*to}\`);
*console.log*(\` - Flow ID: ${DB_CONFIG*.*flow_id}\`);
let *BOT_ID*;
if (CREATE_NEW_BOT) {
*// 5️⃣ CREATE NEW BOT FIRST*
*console.log*("🆕 Creating new bot...");
const *newBotData* = {
*name*: *botData.name*,
*type*: *botData.type* || "tilebot",
*language*: *botData.language* || "en"
};
const *createRes* = *await axios.post*(
\`*https://api.tiledesk.com/v3/*${*PROJECT_ID*}*/faq_kb*\`,
newBotData,
{
*auth*: {
*username*: *EMAIL*,
*password*: *PASSWORD*
},
*headers*: {
"Content-Type": "application/json"
}
}
);
BOT_ID = *createRes.data.*\_id;
*console.log*("✅ New Bot created with ID:", BOT_ID);
*// UPDATE BOT WITH VARIABLES*
*console.log*("🔧 Updating bot with variables...");
const *updateBotRes* = *await axios.put*(
\`*https://api.tiledesk.com/v3/*${*PROJECT_ID*}*/faq_kb/*${*BOT_ID*}\`,
{
*name*: *botData.name*,
*attributes*: {
*variables*: *botData.attributes.variables*
}
},
{
*auth*: {
*username*: *EMAIL*,
*password*: *PASSWORD*
},
*headers*: {
"Content-Type": "application/json"
}
}
);
*console.log*("✅ Bot updated with attributes!");
*console.log*(" Variables set (accessible via Bot Settings → Attributes):");
*Object.keys*(*botData.attributes.*variables)*.forEach*(key => {
*console.log*(\` - ${key}: ${*botData.attributes.*variables\[key\]}\`);
});
} else {
*// 4️⃣ GET EXISTING BOT → BOT ID*
*console.log*("🤖 Fetching bots...");
const *botsRes* = *await axios.get*(
\`*https://api.tiledesk.com/v3/*${*PROJECT_ID*}*/faq_kb*\`,
{
*auth*: {
*username*: *EMAIL*,
*password*: *PASSWORD*
}
}
);
*console.log*("Available bots:");
*botsRes.data.forEach*(b => {
*console.log*(\` - ${*b.*name} (ID: ${*b.*\_id})\`);
});
const *bot* = *botsRes.data.find*(*b* => *b.name* === *ENTERPRISE_BOT_NAME*);
if (!bot) {
*throw* new *Error*(\`Bot '${ENTERPRISE_BOT_NAME}' not found. Available bots: ${*botsRes.data.map*(b => *b.*name)*.join*(', ')}\`);
}
BOT_ID = *bot.*\_id;
*console.log*("✅ Bot ID:", BOT_ID);
}
*// 5️⃣ IMPORT FULL BOT JSON USING /bots/importjson ENDPOINT*
*console.log*("🚀 Importing full bot JSON using /bots/importjson endpoint...");
*console.log*(\` Bot ID: ${BOT_ID}\`);
*console.log*(\` Intents to import: ${*botData.intents?.*length || 0}\`);
*console.log*(\` Payload size: ${JSON*.stringify*(botData)*.*length} bytes\`);
*// Log first intent details for verification*
if (*botData.*intents && *botData.intents.*length > 0) {
const *firstIntent* = *botData.intents*\[0\];
*console.log*(\`\\n First Intent: "${*firstIntent.*intent_display_name}"\`);
*console.log*(\` - Actions: ${*firstIntent.actions?.*length || 0}\`);
if (*firstIntent.*actions && *firstIntent.actions.*length > 0) {
*firstIntent.actions.forEach*((action, idx) => {
*console.log*(\` ${idx + 1}. ${*action.*\_tdActionTitle} (${*action.*\_tdActionType})\`);
if (*action.*\_tdActionType === 'webrequestv2') {
*console.log*(\` URL: ${*action.*url}\`);
*console.log*(\` Headers: ${JSON*.stringify*(*action.*headers)}\`);
*console.log*(\` trueHeaders: ${*action.*trueHeaders ? *action.trueHeaders.*length + ' items' : 'none'}\`);
*console.log*(\` Body Type: ${*action.*bodyType}\`);
*console.log*(\` Form Data: ${*action.formData?.*length || 0} fields\`);
}
});
}
}
*try* {
const *importRes* = *await axios.post*(
\`*https://api.tiledesk.com/v3/*${*PROJECT_ID*}*/bots/importjson/*${*BOT_ID*}\`,
botData,
{
*auth*: {
*username*: *EMAIL*,
*password*: *PASSWORD*
},
*headers*: {
"Content-Type": "application/json"
},
*maxContentLength*: *Infinity*,
*maxBodyLength*: *Infinity*
}
);
*console.log*(\`\\n✅ Bot import successful!\`);
*console.log*(\` Response Status: ${*importRes.*status}\`);
*console.log*(\` Bot Name: ${*importRes.data.*name}\`);
*console.log*(\` Bot ID: ${*importRes.data.*\_id}\`);
} *catch* (importErr) {
*console.error*(\`\\n❌ Bot import failed!\`);
if (*importErr.*response) {
*console.error*(\` Status: ${*importErr.response.*status}\`);
*console.error*(\` Error: ${JSON*.stringify*(*importErr.response.*data)}\`);
} else {
*console.error*(\` Error: ${*importErr.*message}\`);
}
*throw* importErr;
}
*// Wait a moment for the server to process*
*console.log*("\\n⏳ Waiting 3 seconds for server to process import...");
*await* new Promise(resolve => *setTimeout*(resolve, 3000));
*// 6️⃣ VERIFY IMPORT - Check bot details*
*console.log*("\\n🔍 Verifying bot import...");
const *verifyBotRes* = *await axios.get*(
\`*https://api.tiledesk.com/v3/*${*PROJECT_ID*}*/faq_kb/*${*BOT_ID*}\`,
{
*auth*: {
*username*: *EMAIL*,
*password*: *PASSWORD*
}
}
);
*console.log*("📦 Bot Details:");
*console.log*(\` - Bot Name: ${*verifyBotRes.data.*name}\`);
*console.log*(\` - Bot Type: ${*verifyBotRes.data.*type}\`);
*console.log*(\` - Language: ${*verifyBotRes.data.*language}\`);
*// Get all FAQs/intents for this bot*
const *allIntentsRes* = *await axios.get*(
\`*https://api.tiledesk.com/v3/*${*PROJECT_ID*}*/faq?id_faq_kb=*${*BOT_ID*}\`,
{
*auth*: {
*username*: *EMAIL*,
*password*: *PASSWORD*
}
}
);
*console.log*(\`\\n📋 Intents Verification:\`);
*console.log*(\` - Total intents in database: ${*allIntentsRes.data.*length}\`);
*console.log*(\` - Expected intents: ${*botData.intents?.*length || 0}\`);
if (*allIntentsRes.data.*length > 0) {
*console.log*("\\n Intent List:");
*for* (const *intent* of *allIntentsRes.*data) {
*console.log*(\`\\n "${*intent.*intent_display_name}"\`);
*console.log*(\` - ID: ${*intent.*\_id}\`);
*console.log*(\` - Actions: ${*intent.actions?.*length || 0}\`);
if (*intent.*actions && *intent.actions.*length > 0) {
*intent.actions.forEach*((action, idx) => {
*console.log*(\` ${idx + 1}. ${*action.*\_tdActionTitle || 'Unnamed'} (${*action.*\_tdActionType})\`);
if (*action.*\_tdActionType === 'webrequestv2') {
*console.log*(\` - URL: ${*action.*url}\`);
*console.log*(\` - Method: ${*action.*method}\`);
*console.log*(\` - Has headers: ${*action.*headers ? 'Yes' : 'No'}\`);
*console.log*(\` - Has trueHeaders: ${*action.*trueHeaders ? 'Yes (' + *action.trueHeaders.*length + ')' : 'No'}\`);
*console.log*(\` - Body Type: ${*action.*bodyType}\`);
if (*action.*bodyType === 'formData') {
*console.log*(\` - Form Fields: ${*action.formData?.*length || 0}\`);
} else if (*action.*bodyType === 'json') {
*console.log*(\` - Has JSON Body: ${*action.*jsonBody ? 'Yes' : 'No'}\`);
*console.log*(\` - Has trueJsonBody: ${*action.*trueJsonBody ? 'Yes' : 'No'}\`);
}
}
});
}
}
*// Check if our custom intent was imported*
const *customIntent* = *allIntentsRes.data.find*(*i* => *i.intent_display_name* === 'Hi');
if (customIntent && *customIntent.*actions && *customIntent.actions.*length >= 2) {
*console.log*(\`\\n🎉 SUCCESS! Bot imported with full configurations!\`);
*console.log*(\` ✅ Custom intent "Hi" found with ${*customIntent.actions.*length} actions\`);
*console.log*(\` ✅ API integrations are configured and ready to use\`);
} else {
*console.log*(\`\\n⚠️ WARNING: Custom intent may not have been fully imported\`);
}
} else {
*console.log*(\`\\n⚠️ WARNING: No intents found in database after import\`);
}
} catch (err) {
*console.error*("❌ Script failed");
if (*err.*response) {
*console.error*("Status:", *err.response.*status);
*console.error*("Message:", *err.response.*data);
} else {
*console.error*(*err.*message);
}
}
}
run();

