Время создания
Filters
Studio_Creatio
8.0

I'm working in in a cloud-based dev environment. I've created a message template and I'm trying to move it to QA. I've created a package, and I've added the EmailTemplate data to that package. I've added SysImage to the package, too, but adding it or removing it makes no difference to my problem.

When I import the package into my QA environment and open the template in the designer, the panel with the controls for adding blocks, image, text, spacers, etc. are all missing and it views the entire email as one large HTML block.

A package I created yesterday and imported to QA (with a different version of the template)_works fine. I can open the designer with no issues, the individual blocks, images, and text are all individually editable. (I'd use the package I created yesterday, but I thought I no longer needed it so I created a new one.)

When I compare the contents of the exported zip files using Beyond Compare, the one that works has a JSON element like the below (I only include the first part...it's a long string).

"Value": "{\"ItemType\":\"sheet\",\"Caption\":\"Content designer\",\"Width\":600,\"BackgroundColor\":\"#ffffff\",\

I'm assuming the fact the JSON with Content Designer missing is a part of my problem (there are other differences in the package, too - a large number of references to .csproj files that my broken package doesn't have). But I can't figure out why one package included it and the other doesn't. Anyone have an idea?

Like 0

Like

2 comments
Best reply

Hello Jeff,

I suggest you to manually move the template from your dev-site to to your QA. You can easily download the template information and import it using OData.

  1. Copy the email template Id that you want to move
  2. Access the email template information in the EmailTemplate table:
    https://your-dev-site.creatio.com/0/odata/EmailTemplate(template_id)

    You will need to take this JSON and remove the lines that have "@odata." in the name (the first line the last 12 lines) as they are not needed for the import. If you have any fields with value "00000000-0000-0000-0000-000000000000", you will need to remove those lines too. For this example, the result parsed JSON would be:

    {
        "Id": "0dc0759c-80b3-48b3-a832-7e32925d748b",
        "CreatedOn": "2019-07-15T23:30:13.367747Z",
        "CreatedById": "410006e1-ca4e-4502-a9ec-e54d922d2c00",
        "ModifiedOn": "2025-12-16T20:55:28.568102Z",
        "ModifiedById": "410006e1-ca4e-4502-a9ec-e54d922d2c00",
        "ProcessListeners": 0,
        "Name": "Case assigned to group",
        "Subject": "Case #[#Number#] \"[#Subject#]\" assigned to group",
        "Body": "<div>\n\t<div>\n\t\t<span style=\"font-size:12px;\"><span style=\"font-family:arial,helvetica,sans-serif;\">Case <strong>#[#Number#]</strong> \"[#Subject#]\" has been assigned to your group.</em></span></span></div>\n\t<div>\n\t\t </div>\n\t<div>\n\t\t\t<span style=\"font-size:12px;\"><span style=\"font-family:arial,helvetica,sans-serif;\"><strong>Priority:</strong> [#Priority.Name#]</span></span></div>\n\t<div>\n\t\t\t </div>\n\t<div>\n\t\t\t<span style=\"font-size:12px;\"><span style=\"font-family:arial,helvetica,sans-serif;\"><strong>Response deadline:</strong> [#ResponseDate#];</span></span></div>\n\t<div>\n\t\t </div>\n\t<div>\n\t\t\t<span style=\"font-size:12px;\"><span style=\"font-family:arial,helvetica,sans-serif;\"><strong>Resolution deadline:</strong> [#SolutionDate#].</span></span></div>\n\t\t<div>\n\t\t\t </div>\n\t<div>\n\t\t\t<span style=\"font-size:12px;\"><span style=\"font-family:arial,helvetica,sans-serif;\"><strong>Contact:</strong> [#Contact.Name#].</span></span></div>\n\t\t<div>\n\t\t\t </div>\n\t<div>\n\t\t\t<span style=\"font-size:12px;\"><span style=\"font-family:arial,helvetica,sans-serif;\"><strong>Account:</strong> [#Account.Name#].</span></span></div>\n\t</div><div> </div>",
        "IsHtmlBody": false,
        "SendIndividualEmailId": "e75ac3fe-be9b-4a01-87db-c7dffd354f8c",
        "SaveAsActivity": false,
        "ShowBeforeSending": false,
        "TemplateConfig": "",
        "ConfigType": 0,
        "Notes": "",
        "TemplateTypeId": "74ff0cee-6593-482f-a62f-6dde6e17ab5e"
    }
  3. Send all this information to your QA site:
    1. If you want to update the existing template, send a PATCH request to https://your-qa-site.creatio.com/0/odata/EmailTemplate(template_id) with the parsed JSON in the body without the Id field
    2. If you want to re-create the template, remove it first and then send a POST request to https://your-qa-site.creatio.com/0/odata/EmailTemplate with the all the parsed JSON in the body.

I have moved a lot of templates from many sites using this method and it is by far the simplest for me when I want to have an exact copy between them. As you already moved the SysImage information, you won't need to migrate anything from that table using this method.

You can find more information about Creatio OData here:

 

Regards

Hello Jeff,

I suggest you to manually move the template from your dev-site to to your QA. You can easily download the template information and import it using OData.

  1. Copy the email template Id that you want to move
  2. Access the email template information in the EmailTemplate table:
    https://your-dev-site.creatio.com/0/odata/EmailTemplate(template_id)

    You will need to take this JSON and remove the lines that have "@odata." in the name (the first line the last 12 lines) as they are not needed for the import. If you have any fields with value "00000000-0000-0000-0000-000000000000", you will need to remove those lines too. For this example, the result parsed JSON would be:

    {
        "Id": "0dc0759c-80b3-48b3-a832-7e32925d748b",
        "CreatedOn": "2019-07-15T23:30:13.367747Z",
        "CreatedById": "410006e1-ca4e-4502-a9ec-e54d922d2c00",
        "ModifiedOn": "2025-12-16T20:55:28.568102Z",
        "ModifiedById": "410006e1-ca4e-4502-a9ec-e54d922d2c00",
        "ProcessListeners": 0,
        "Name": "Case assigned to group",
        "Subject": "Case #[#Number#] \"[#Subject#]\" assigned to group",
        "Body": "<div>\n\t<div>\n\t\t<span style=\"font-size:12px;\"><span style=\"font-family:arial,helvetica,sans-serif;\">Case <strong>#[#Number#]</strong> \"[#Subject#]\" has been assigned to your group.</em></span></span></div>\n\t<div>\n\t\t </div>\n\t<div>\n\t\t\t<span style=\"font-size:12px;\"><span style=\"font-family:arial,helvetica,sans-serif;\"><strong>Priority:</strong> [#Priority.Name#]</span></span></div>\n\t<div>\n\t\t\t </div>\n\t<div>\n\t\t\t<span style=\"font-size:12px;\"><span style=\"font-family:arial,helvetica,sans-serif;\"><strong>Response deadline:</strong> [#ResponseDate#];</span></span></div>\n\t<div>\n\t\t </div>\n\t<div>\n\t\t\t<span style=\"font-size:12px;\"><span style=\"font-family:arial,helvetica,sans-serif;\"><strong>Resolution deadline:</strong> [#SolutionDate#].</span></span></div>\n\t\t<div>\n\t\t\t </div>\n\t<div>\n\t\t\t<span style=\"font-size:12px;\"><span style=\"font-family:arial,helvetica,sans-serif;\"><strong>Contact:</strong> [#Contact.Name#].</span></span></div>\n\t\t<div>\n\t\t\t </div>\n\t<div>\n\t\t\t<span style=\"font-size:12px;\"><span style=\"font-family:arial,helvetica,sans-serif;\"><strong>Account:</strong> [#Account.Name#].</span></span></div>\n\t</div><div> </div>",
        "IsHtmlBody": false,
        "SendIndividualEmailId": "e75ac3fe-be9b-4a01-87db-c7dffd354f8c",
        "SaveAsActivity": false,
        "ShowBeforeSending": false,
        "TemplateConfig": "",
        "ConfigType": 0,
        "Notes": "",
        "TemplateTypeId": "74ff0cee-6593-482f-a62f-6dde6e17ab5e"
    }
  3. Send all this information to your QA site:
    1. If you want to update the existing template, send a PATCH request to https://your-qa-site.creatio.com/0/odata/EmailTemplate(template_id) with the parsed JSON in the body without the Id field
    2. If you want to re-create the template, remove it first and then send a POST request to https://your-qa-site.creatio.com/0/odata/EmailTemplate with the all the parsed JSON in the body.

I have moved a lot of templates from many sites using this method and it is by far the simplest for me when I want to have an exact copy between them. As you already moved the SysImage information, you won't need to migrate anything from that table using this method.

You can find more information about Creatio OData here:

 

Regards

Alejandro González Momblán,

Thank you 100x!

I missed removing some of the odata on my first attempt at an insert but got past that pretty quickly.

Thanks again and I really appreciate this community!

Show all comments
tile_view
bulk emails
Feature Requests
FreedomUI

Hi, I saw a post from mid-2024 mentioning that this feature might be added. I just wanted to check whether it is actually being developed and planned for bulk emails.

Having the tile view option alongside list view has been great and is definitely my preferred way of working. I know our organisation intends to transition to the Freedom UI for this app, but I was hoping this feature would be available before that transition. At the moment, the Freedom UI doesn’t include this option for displaying data, which feels like a step backwards.

Like 0

Like

2 comments

I do miss the tile layout for lists in Freedom UI (along with seeing the record logo/image). I don't currently see that on the roadmap, sadly. While not exactly the same thing but thought I'd mention, multi-line lists are slated for Q1 2026. 

Ryan

Thank you! Figured as much. That is good to know!

Show all comments
folders
folderpane
Sales_Creatio
8.0

Hello, we recently converted to FreedomUI and was wondering is there a way that our end users can adjust the size of the folder pane, like you could in ClassicUI? I know on the designer I can adjust the size, but would like to have our end users have the ability that they once had.

Like 0

Like

1 comments

Hello,

Unfortunately, it's not possible to adjust the size of the folder panel at this time. However, we've registered this idea in our R&D team backlog for consideration and implementation in future application releases. 
 

Thank you for helping us to improve our product.
 

Show all comments
Sales_Creatio
8.0

I am trying to figure out how my system is validating fields on an Opportunity.  We have two required date fields on the page, one for the closing date and another to capture the date the User is expecting the first shipment if the Opportunity is won.  

Currently, our validation rules require that the date the User enters in the first shipment field must be AFTER the close date (e.g. If closing date = n then the first shipment date must be at least n+1).  I want to adjust the rule so that the first shipment date can be the same as the closing date or later.

When I look for the validation section of the Opportunity page with the two fields I find this at the bottom of the script:

The academy shows a different script when describing validation schema (see below) which leaves me at a bit of a dead-end.  Any idea how validations might be implemented and how I might access the code?

Thanks in advance!

 

Like 0

Like

1 comments

Good day, Greg,

Thank you for reaching out to us. 

If we understand you correctly, the Academy page you are consulting with is "Implement the validation of a field value on a page". This article describes how code-based field validation can be implemented in Freedom UI and it only shows examples of such implementations.

If you are not locating any code in "validators" of your Opportunity pages, please make sure that:

  1. The logic is implemented in Freedom UI
  2. The logic is not implemented by some other means, such as Business Rules
  3. That you are verifying the code in the correct package.
    For example, out of the box, the Opportunities_FormPage code is spread out across several packages:
    Opportunities_FormPage

If the implementation is done via code, it may be located in a custom package that is not in the last position in the hierarchy. Alternative code-based methods of validation implementation may also have been implemented on your site. 

In the event you are still facing issues locating the logic responsible for date validation, please contact us on our <support@creatio.com> address so we may connect to your environment and examine its setup.

Thank you for reaching out to us!

Show all comments

Hi Team,

There is an Usecase where I have to call Business Process from web service. In Creatio, we have 2 types of service: cookie-based & Anonymous. I have tried both type of services to invoke Business Process, but getting below error: 401

 

Query: Can you guide me how to call BP using Anonymous & Cookie Based Web-service ?

(Cookie-Based Service)

[OperationContract]
[WebGet(UriTemplate = "UnsubscribeStatusBP?ContactId={ContactId}&BulkemailId={BulkemailId}&EndUserResult={EndUserResult}", ResponseFormat = WebMessageFormat.Json)]
public string UnsubscribeStatusBP(string ContactId, string BulkemailId, string EndUserResult)
        {
            // 1. Get UserConnection from the BaseService context
            UserConnection userConnection = UserConnection;
            IProcessExecutor processExecutor = userConnection.ProcessEngine.ProcessExecutor;
            // SessionHelper.SpecifyWebOperationIdentity(HttpContextAccessor.GetInstance(), SystemUserConnection.CurrentUser);
            
            // 1. Validate ContactId (GUID)
            if (!Guid.TryParse(ContactId, out Guid contactGuid)) {
                return JsonConvert.SerializeObject(new { Success = false, Message = "❌ Invalid Parameter ContactId. Must be a valid GUID." });
            }
        
            // 2. Validate BulkemailId (GUID)
            if (!Guid.TryParse(BulkemailId, out Guid bulkemailGuid)) {
                return JsonConvert.SerializeObject(new { Success = false, Message = "❌ Invalid Parameter 'BulkemailId'. Must be a valid GUID." });
            }
        
            // 3. Validate EndUserResult (String length, null check, and basic sanitization)
            if (string.IsNullOrWhiteSpace(EndUserResult) || EndUserResult.Length > 10) {
                return JsonConvert.SerializeObject(new { Success = false, Message = "❌ Invalid Parameter 'EndUserResult'. Must be 1-10 characters." });
            }

            string sanitizedResult = Regex.Replace(EndUserResult, @"[^a-zA-Z_]", "");
            
            try {
                // 1. Prepare parameters
                var inputs = new Dictionary {
                    { "ContactId", contactGuid },
                    { "BulkemailId", bulkemailGuid },
                    { "EndUserResult", sanitizedResult }
                };
         
                // 2. Execute process via ProcessEngine
        
                userConnection.RunProcess(
                    "UsrAllManageUnsubscribeStatus", 
                    inputs
                );
         
                return JsonConvert.SerializeObject(new { Success = true, Message = "✅ Business Process is triggered." });
            }
            catch (Exception ex) {
                return JsonConvert.SerializeObject(new { Success = false, Message = $"❌ {ex.Message}" });
            }
        }


(Anonymous Web-Service)

[OperationContract]
[WebGet(UriTemplate = "UnsubscribeStatusBP?ContactId={ContactId}&BulkemailId={BulkemailId}&EndUserResult={EndUserResult}", ResponseFormat = WebMessageFormat.Json)]
public string UnsubscribeStatusBP(string ContactId, string BulkemailId, string EndUserResult)
{
	// 1. MUST BE FIRST: Set the identity to SystemUser for anonymous calls
	// This connects the "guest" request to a real user account (Supervisor) internally
	SessionHelper.SpecifyWebOperationIdentity(HttpContextAccessor.GetInstance(), SystemUserConnection.CurrentUser);
 
	string result = "{}";
 
	// 1. Validate ContactId (GUID)
	if (!Guid.TryParse(ContactId, out Guid contactGuid)) {
		return JsonConvert.SerializeObject(new { Success = false, Message = "❌ Invalid Parameter ContactId. Must be a valid GUID." });
	}
 
	// 2. Validate BulkemailId (GUID)
	if (!Guid.TryParse(BulkemailId, out Guid bulkemailGuid)) {
		return JsonConvert.SerializeObject(new { Success = false, Message = "❌ Invalid Parameter 'BulkemailId'. Must be a valid GUID." });
	}
 
	// 3. Validate EndUserResult (String length, null check, and basic sanitization)
	if (string.IsNullOrWhiteSpace(EndUserResult) || EndUserResult.Length > 10) {
		return JsonConvert.SerializeObject(new { Success = false, Message = "❌ Invalid Parameter 'EndUserResult'. Must be 1-10 characters." });
	}
 
	string sanitizedResult = Regex.Replace(EndUserResult, @"[^a-zA-Z_]", "");
 
	try {
		// 1. Prepare parameters
		var inputs = new Dictionary<string, object> {
			{ "ContactId", contactGuid },
			{ "BulkemailId", bulkemailGuid },
			{ "EndUserResult", sanitizedResult }
		};
 
		// 2. Execute process via ProcessEngine
 
		SystemUserConnection.RunProcess(
			"UsrAllManageUnsubscribeStatus", 
			inputs
		);
 
		return JsonConvert.SerializeObject(new { Success = true, Message = "✅ Business Process is triggered." });
	}
	catch (Exception ex) {
		return JsonConvert.SerializeObject(new { Success = false, Message = $"❌ {ex.Message}" });
	}
}															
Like 0

Like

4 comments

Hello!

Please refer to the Creatio documentation on custom web services that use cookie-based authentication. This type of web service requires an authenticated HTTP session, which is why you are receiving a 401 error.

Eduard Dovydovskyi,

Hi Eduard,

Anonymous Web-Service: UsrUnsubscribeEmailService
Usecase: There is an anonymous endpoint 'GetHtmlPage' that is rendering HTML Web-Page (below image).


When user press confirm button then It should call Business Process to perform some action. To achieve this, I have created another anonymous endpoint 'RunBP' to call Business Process.

(Endpoint: GetHtmlPage)

(using JavaScript inside Html page to call endpoint `RunBP` on button click):

fetch(`/0/rest/UsrUnsubscribeEmailService/RunBP`, {

   method: 'GET',

    headers: { 

        'Content-Type': 'application/json'

    }

})

.then(response => response.json())

.then(data => {

    console.log('Server Response: ', data);

})

.catch(err => {

    console.error('Fetch error:', err);

});

 

(Endpoint: RunBP)

[OperationContract]

[WebInvoke(Method = "GET", UriTemplate = "RunBP", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]

public string RunBP()

{

    try

    {

        IProcessEngine processEngine = SystemUserConnection.ProcessEngine;

        IProcessExecutor processExecutor = processEngine.ProcessExecutor;

        processExecutor.Execute("UsrTest");

        return "SUCCESS";

    }

    catch (Exception ex)

    {

        return "ERROR: " + ex.Message;

   }

}

Note: (UsrTest business process is for testing purpose only)

 

(Error: 401 Unauthorized)


Query: Guide me how can I call business Process from 

  1. Anonymous Service
  2. Cookied-Based Service (If required), then how to connect or call this service from anonymous service not to get 401 error 

     

souresh khandelwal,

Does this all work if you're in an incognito window? 

I have an anonymous webservice that is used this way as well. However, if my browser session is already logged into Creatio, I get the 401 error as well, but worked if I wasn't already logged in to Creatio in the browser session (or using an incognito window). I suppose if there's an active session for the user it's expecting the CSRF header as well, even when calling it via the anonymous endpoint address.

To resolve this, I check to see if there is a cookie named BPMCSRF for the user and if so I add it as a BPMCSRF header in the request. Then it works whether my browser is logged into Creatio or not and it works both ways.

The code to check for the cookie I use is something like this:

function checkBpmCookie() {
	var name = "BPMCSRF=";
	var cookieParts = document.cookie.split(';');
	for (var i = 0; i &lt; cookieParts.length; i++) {
		var cookie = cookieParts[i];
		while (cookie.charAt(0) == ' ') {
			cookie = cookie.substring(1);
		}
		if (cookie.indexOf(name) == 0) {
			return cookie.substring(name.length, cookie.length);
		}
	}
	return "";
}

And then in my request from the page to the service (in my case this is an older page that uses jQuery)

var postPayload = {
	url: "https://" + host + "/0/ServiceModel/CfxSubscriptionService.svc/SaveSubscriptions",
	method: "POST",
	timeout: 0,
	headers: {
		"Accept": "application/json",
		"Content-Type": "application/json"
	},
	data: JSON.stringify(data),
};
 
var bpmCsrf = checkBpmCookie();
if (bpmCsrf) {
	postPayload.headers.BPMCSRF = bpmCsrf;
}
 
$.ajax(postPayload).done(function (data) {
	//...
}

Ryan

souresh khandelwal,

You are calling your endpoint using cookie-based authentication via /0/rest/ServiceName/EndpointName, that is why you are getting a 401 error.

Anonymous services are called via /0/ServiceModel/ServiceName.svc/EndpointName.

Please refer to the documentation.

Show all comments