Время создания
Filters
webhooks
webhook
businessProcess

Creatio’s webhook service will technically accept every webhook you throw at it, regardless of whether the formatting matches the existing process structure. Processing, however, is very particular about format. Many times, you will receive webhooks from a source with no control over payload structure. Some customers turn to middleware tools to reformat the payload, however, you can create some psuedo-ETL (extract-transform-load) business processes in Creatio using OOTB tools to manage your payloads as well. To do this, you can create your own processes to handle the incoming webhook records, based on the OOTB webhook business processes. This guide is not intended to be an exhaustive solution, but merely an intro to the OOTB webhook components and how you can use some creativity in combination with Creatio’s business processes to work around your payload structure difficulties. This guide is also intended to be a foundation. I encourage you to expand, grow, and add your own scale/complexity as needed.

 

First, you should note the key processes of webhook consumption in Creatio. I assume you already have webhooks configured, and if not, that is already well documented, so I won’t get into that piece here.

  1. Start process to create object records based on incoming webhooks
    This is the first business process used to process the incoming webhooks. The OOTB business process runs on a timed trigger with a 1 minute interval. This process immediately calls the following via subprocess and nothing more. This first process is simply a trigger. (Arguably, redundant, as you could’ve just started your 1min trigger on #2 below, which I will recommend later when we build our custom replacements.)
    1. Engagement tools App: Start process to create object records based on incoming webhooks
      The name above is an alternative version that appears in new versions of Creatio. If you have the Engagement App version, then you will likely have BOTH versions available with one disabled. The Engagement App version includes an additional subprocess: Define contact based on submitted form.
      For simplicity, we will ignore this flow for now. I’ll address this at the end, as there’s many ways to incorporate the find/create contact processes.

 

  1. Create object records based on incoming webhooks
    This is where the magic happens. This process reads all webhooks that are in a “New” status, indicating they need to be processed. To process the webhooks, Creatio calls a user task “Webhook To Entity UT”, which translates the incoming JSON payload to the appropriate objects and fields identified. This assumes you’ve strictly followed the Creatio webhook format – and if not, then your webhook stalls here and fails to process. This user task handles every part of webhook processing, from identifying the appropriate object, the fields, and the status of the incoming webhook record/payload itself.

We will disable both business processes above and create our own to handle our custom logic.

 

Start with a stable foundation.
The second process of Creatio’s webhook pair is the one with all the juice. We can simplify and combine these two as mentioned previously. Likewise, we want to keep Creatio’s OOTB webhook processing in place, so we’ll start with a copy of process number 2.

  1. Open the process “Create object records based on incoming webhooks”
  2. Select Actions -> Copy Diagram
  3. Save this new process with a new title and code as desired. Otherwise, you can also just use a “Custom - ” or “Usr” prefix – whatever is relevant for your environment. Be sure to set your package appropriately as well.

 

The customization.
Now that we have our foundation, we can start to make the necessary adjustments.

  1. The trigger
    This process starts with a manual trigger. We have two options here: timer or trigger.

    • Trigger
      My personal preference in all scenarios is to convert these to a trigger. Not only will this help to keep your logs from bloating with processes that run every 1 min doing nothing, but this will also simplify our processing for ETL, as we need to read the incoming payload to determine how to route the record through our new process. For a trigger, set the object to Webhook and event to “Record added”. For added security, we’ll set a condition on this for “Status = New” as well.


       
    • Timer
      If you regularly receive a very high volume of webhooks (several per minute, constantly, throughout the day), then stick with the 1 min interval. For most, running 1 min timers on batch is overkill. Likewise, this will complicate our ETL process as we would have to add an additional read collection element with our new criteria, alongside another subprocess to manage the collection of incoming records. I’ve documented this for reference, but we will not be using timer for this guide.

     

  2. Webhook read
    The OOTB process, built for handling webhooks in batch, uses a “Read collection” element. The OOTB user task “Webhook to Entity UT” expects a collection for input. For simplicity, we’re going to keep this node but simply move to later in the process. We’ll also need to read the newly triggered record as well.

    • Copy the “Read collection”, paste this just after the trigger, then update this element to “Read the first record in the selection”
    • Set the label to “Read new webhook”
    • Update the filter to read the incoming trigger Id

     

  3. Conditional evaluation
    The ‘OR’ element currently evaluates for webhooks “yes or no”. Since we moved to a trigger, this is always yes. However, we want to sort based on OOTB behavior vs. our custom behavior. We’ll keep the OR element but update the label and conditions. Since we are reading each record independently, we can analyze in incoming request body to determine whether the format matches Creatio’s suggested format OR some other format. Creatio’s payloads always start with {"EntityName": which we will leverage in our conditional evaluation.
    • Change the “Conditional flow” evaluation from the existing “Count” logic to our new behavior determining whether or not the payload is an OOTB Creatio format or something else:
      [#Read new webhook.First item of resulting collection.Request body#].StartsWith("{\"EntityName\"")
    • The NO path is where we will begin to integrate our custom logic for our new ETL process.
  4. ETL process
    You can use strictly a process alone to manage the webhook behavior, without any additional objects/tables, though this tends to get messy and requires a lot of C# formula steps and substrings. However, if your source is completely inflexible then you may need to go on this route. This does get a bit messy and is inherently sensitive to incoming data format as we’re doing simple string manipulation/extraction but can be reliable assuming the data structure remains the same. Likewise, if your incoming data structure were to change for whatever reason, you’d likely still have an issue importing your payload regardless of method of extraction.

    In some instances, you might even be able to create the entire Creatio structure as desired, but perhaps there are some additional field wrappers preventing direct ingestion. This step will require you to conduct your own analysis, development, and testing for your unique incoming payload.

    In this example, I’m assuming that I have an incoming lead from a web form, so I want to grab a few key data points like first and last name, phone, email. I might also have information like interested products, notes, as well. Alternatively, this webform might be uniquely specific to a certain product, like a Mortgage. In that case, even though the payload may not have the info, I can still set those values in the process.
    • Create a new subprocess that will manage the logic for our unique payload format.
    • Create a new input parameter for “Request body” so that we can pass this in from the parent record.
    • Define the fields we want to extract from the request body by creating a “parameter” for each field.
    • Set the value of each parameter to a C# string formula to extract the desired values. You will need to adjust this based on your use case. I’ve included an example payload and formula string for reference:
      • [#Request body#].Substring(([#Request body#].IndexOf("\"name\":\"") + 8), ([#Request body#].IndexOf("\"", ([#Request body#].IndexOf("\"name\":\"") + 8)) - ([#Request body#].IndexOf("\"name\":\"") + 8)))
      • You can use this formula, adjusting the field name and offsets for the desired incoming fields. Name, consisting of 4 characters, plus 4 from 3 quotations and a colon. First name, by contrast, is 10 characters, plus the four from quotations and the colon. Be sure to account for any other characters or spaces when constructing your Substring formula.
      • [#Request body#].Substring(([#Request body#].IndexOf("\"First Name\":\"") + 14), ([#Request body#].IndexOf("\"", ([#Request body#].IndexOf("\"First Name\":\"") + 14)) - ([#Request body#].IndexOf("\"First Name\":\"") + 14)))


         
    • Now that we’ve defined our field values in the parameter formulas, we can drop in an “Add data” node, setting the fields as desired. In this case, we’re going to create a new Lead record, using a few of the relevant fields from our payload: Name (as lead name), first name, last name, and phone (which we will map to email as this appears to have been repurposed on the web form based on incoming data). This part is nice and simple – drop the node, select the Lead object, add the relevant fields, then map each of those to the parameters we created.
    • With this process completed / saved, move back to our “parent” process with the trigger and add this as a subprocess. Set the “Request body” parameter to the value of the “Read new webhook” element’s “Request body”.

    • Now that we’ve processed our payload, we need to update the status of the webhook appropriately. To do so, we’ll add a new node after the subprocess to set the webhook record’s status to “done”.
    • Finally, we need to make sure to update the status of failed webhooks in our “default path”. Since we’re adding logic to conditionally skip the OOTB Webhook user task, we want to include some redundancy of our own. In this case, we failed to register the webhook as an OOTB payload, we failed to match our condition for the lead subprocess, so we need to set the status to “Failed.”

 

Extra credit – find / create contact records
Remember my mention of the “Engagement App” version of the process? That version includes some additional logic for tracking UTM information. This can be helpful depending on your implementation. Likely, a Marketing customer will want to track this info. If so, I suggest getting familiar with that subprocess and incorporating this into your new custom webhook processor. Even if you’re not a Marketing user, there’s another subprocess buried in these subprocesses that is beneficial for everyone: Searching and creating contact.

This process allows for input parameters for common contact information data points, then will identify a matching contact based on the determined logic, returning the contact Id for matched existing contact records or will create a new contact record based on the searched values. Most likely, you will want this. You’ll get better insights to leads that you’ve had prior interactions with, establish history with new contacts, and generally just improve your data quality. Adding this to our subprocess is quite simple.

  • Return to our ETL subprocess, then add a new “Subprocess” node, selecting the “Searching and creating contact” process from the list.
  • Define our input parameters on this node, allowing the subprocess to find/create the contact as appropriate.
  • Update the “Add data” node. Now that we are working with Contact records, rather than string fields, we can simply pass the Id from the find/create subprocess, as this will have the contact info we provided. As such, you can remove the name/phone/email fields from the Lead record, as that would be redundant alongside the contact record.

 

Final cleanup

Be sure to disable the old webhook processes…

  • Start process to create object records based on incoming webhooks
  • Engagement tools App: Start process to create object records based on incoming webhooks

…and enable our new processes

  • Custom - Create object records based on incoming webhooks
  • Custom - Webhook - New lead from website
Like 3

Like

Share

0 comments
Show all comments

Passing Data Between Freedom UI Pages in Creatio (Real Project Case)

Introduction

This article is based on a real Freedom UI use case, not a hypothetical demo.

The requirement comes from an actual configuration scenario:

  • A Freedom UI form page (UsrSmartFilter_FormPage)
  • A custom Freedom UI component/page (UsrColumnViewerComponent)
  • The need to pass runtime values (object name + record Id)
  • And then persist user selections back into the database

No sandbox. No assumptions. Only what works in Freedom UI today.

Real Problem Statement

In a Freedom UI implementation, the user:

  1. Opens UsrSmartFilter_FormPage
  2. Selects an object (for example: Account, Contact, etc.)
  3. Clicks a button to configure columns
  4. A second Freedom UI page/component opens
  5. That page must:
    • Know which object was selected
    • Know which UsrSmartFilter record is being edited
    • Show all columns of the selected object
    • Save selected column names back to UsrSmartFilter.UsrColumnNames_Values

Classic UI sandbox messaging cannot be used.

Why Sandbox and Page Parameters Cannot Be Used

In Freedom UI:

  • Pages are isolated
  • There is no exposed sandbox API
  • Parameters passed via crt.OpenPageRequest are not injected into the target page context

This is a documented and observed platform behavior, not a theory.

The Working Mechanism: BroadcastChannel

The solution that works reliably is using the browser-native <strong>BroadcastChannel</strong> API.

This is not a Creatio abstraction. It is a standard Web API that works because:

  • Freedom UI pages run in the same browser context
  • Messaging happens at the browser level
  • No Creatio internals are bypassed

Real Sender: UsrSmartFilter_FormPage Handler

This handler exists on UsrSmartFilter_FormPage and is triggered by a button click.

What It Sends

  • Selected object schema name
  • Current page record Id (UsrSmartFilter.Id)

Real Handler Code

{
	request: "QNT.GetColumns",
	handler: async (request, next) => {
		const objectValue = request.$context.PDS_UsrSelectedObject_n4di2v4;
		const pageId = request.$context.Id;
 
		if (!objectValue || !pageId) {
			return next?.handle(request);
		}
 
		const channel = new BroadcastChannel("UsrSmartFilter_Channel");
 
		channel.postMessage({
			selectedObjectName: objectValue.displayValue,
			pageId: pageId
		});
 
		channel.close();
 
		return next?.handle(request);
	}
}

This code is taken directly from a working Freedom UI page.

Real Receiver: UsrColumnViewerComponent

UsrColumnViewerComponent is a custom Freedom UI component responsible for:

  • Receiving the object name and page Id
  • Loading the selected object schema dynamically
  • Allowing the user to select columns
  • Persisting the selection

Receiving the Data

this._channelIn = new BroadcastChannel("UsrSmartFilter_Channel");
 
this._channelIn.onmessage = async (event) => {
	const { selectedObjectName, pageId } = event.data || {};
 
	this._selectedObjectName = selectedObjectName;
	this._parentPageId = pageId;
 
	await this._loadObjectSchema();
	this._render();
};

This logic runs when the component is initialized.

Loading the Object Schema Dynamically

Instead of hardcoding Account, the schema is loaded dynamically using the received object name:

const model = await sdk.Model.create(this._selectedObjectName);
const schema = await model.getSchema();
 
this._columns = Object.values(schema.attributes).map(attr => ({
	name: attr.name,
	caption: attr.caption || attr.name
}));

This is standard Freedom UI Model API usage.

Persisting the Selected Columns (Real Persistence)

When the user selects or unselects columns, the component:

  1. Builds a comma-separated string
  2. Updates the existing UsrSmartFilter record
const model = await sdk.Model.create("UsrSmartFilter");
 
await model.update({
	Id: this._parentPageId,
	UsrColumnNames_Values: columnsCsv
});

This is not messaging — this is actual data persistence.

Why This Is a Real, Production-Safe Pattern

  • Uses only documented Web APIs
  • Uses Creatio Freedom UI Model API
  • No reliance on undocumented sandbox features
  • Works across page reloads once persisted

This pattern has been validated in real projects.

Key Constraints (Real, Not Theoretical)

  • BroadcastChannel does not queue messages
  • Receiver must be initialized first
  • Transient data should always be persisted

Ignoring these leads to real bugs.

Conclusion

This article does not describe a hypothetical demo.

It documents a real Freedom UI implementation pattern for:

  • Passing data between pages
  • Dynamically loading object metadata
  • Persisting user configuration

Until Creatio provides an official sandbox replacement for Freedom UI, this is the correct approach.

Final Takeaway

Freedom UI pages do not share state.
Browser-level messaging + Model API persistence is the only reliable solution today.

This is based on actual working code, not assumptions.

Like 2

Like

Share

1 comments

Thanks for this write up. I've been using the native BroadcastChannel API for communication in Freedom UI and works fantastically!

It's important to make sure the receiver also gets disconnected to allow garbage collection. For the sender, it's easy to just call channel.close(); after sending, but the listener is typically initialized in the crt.HandleViewModelInitRequest on the receiving page. For this, I will put a reference to the channel object in an attribute on the receiving page (how to use attributes) and then dispose/close in the crt.HandleViewModelDestroyRequest:

{
    request: "crt.HandleViewModelDestroyRequest",
    handler: async (request, next) =&gt; {
        const channel = await request.$context.MyChannelAttr;
        if (channel) {
        	channel.close();
        	request.$context.MyChannelAttr = null;
        }
        return next?.handle(request);
    }
}

Ryan

Show all comments
Marketing_Creatio
marketplace
marketplace_updates
CreatioAI
agents
AIAgents

First AI Agents hit the Marketplace 🚀

We’re excited to share the first successful results of our AI Agent Pilot Program cohort. As part of Creatio’s vision for the AI-native era of Enterprise automation, we launched this pilot at the end of autumn to help partners accelerate the adoption of our new Agentic Framework.

Just a few weeks later, the results are already outstanding - three new AI Agents have been successfully published on the Creatio Marketplace.

Want more? ➡ Browse all Agents
 

Build your own AI Agent with us 🤖

If you're looking to turn AI-driven automation ideas into revenue, join the next wave and build your Agent in just a few weeks.

Partner benefits 🤝

   Dedicated development support

   Peer community & expert-led training

   Go-to-market support & early-adopter visibility

Build your AI Agent
Like 6

Like

Share

0 comments
Show all comments
#CreatioAcademy
#TechHour
#8.3.2

Next week! Join our next Tech Hour sessions on 18 and 19 December where we’ll unpack what’s new in the fresh Creatio 8.3.2 release.
Explore the latest Creatio.ai enhancements, discover new CRM capabilities apps Territory Management, and see what’s improved in Freedom UI.

Be the first to dive into the updates.
👉 Save your spot and join the session!
Register here>>>

Like 3

Like

Share

0 comments
Show all comments

We’re excited to introduce a new enhancement designed to help partners gain more visibility and deliver better user experiences on the Creatio Marketplace. Many listings today lack Key Features, which makes it harder for our search engine to accurately match users with the most relevant solutions. To address this, we’re rolling out AI-generated key features for listings that currently don’t have them.

How to Boost Your Listing’s Visibility

1.   Review the AI-generated key features in your Marketplace Console.

2.   Refine the text — adjust descriptions so they better reflect your solution.

3.   Add a screenshot to complete the key feature.

4.   Publish the feature to make it visible to customers and strengthen your app’s presentation.

Review AI-generated Key Features

Take 2 minutes now — this directly impacts how often your solution appears in customer searches.

Marketplace updates: AI-Generated Key Features to Improve Marketplace Search

Additionally, we’re introducing a manual key feature generation tool in the Marketplace Console. This capability will be available soon for all partners, giving you full control to craft or regenerate key features based on your listing data.

Let’s work together to make the Creatio Marketplace a more searchable, discoverable, and high-performing ecosystem.

If you encounter any questions or issues along the way, please do not hesitate to reach out to us at marketplace@creatio.com.

Like 8

Like

Share

0 comments
Show all comments