Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Appmixer is an embedded iPaaS (Integration Platform as a Service) platform designed for SaaS vendors. It enables them to launch native integrations and no-code automations within their products. Appmixer significantly accelerates the time required to deliver integrations to customers.
There are three main use cases for Appmixer.
Instantly deliver an integration marketplace within your SaaS product using Appmixer Studio and its no-code drag-and-drop designer. Pre-build integrations that enable your end-users to connect your product with other third-party apps and APIs. This feature allows for seamless integration and enhances user experience by facilitating easy connections with external applications.
Enhance your product with embedded workflow automation that offers no-code capabilities. This feature empowers your end-users to visually automate their workflows, customize your product, and connect to any third-party application, facilitating a more versatile and user-friendly experience.
Leverage the Appmixer no-code automation designer to streamline processes within your company, all while eliminating the need to write any code.
Being equipped to troubleshoot your customers' integrations is crucial, as there are myriad reasons why integrations might encounter issues. The most frequent problems stem from integration misconfigurations, your end-users revoking previously granted permissions, alterations in third-party apps leading to the use of outdated data, service outages, and various other factors. When your customers encounter difficulties, it's essential to have the necessary tools to investigate and understand the causes of these issues. This tutorial is designed to provide you with the knowledge and resources needed for effective troubleshooting.
To access information on your end-users, navigate to the "Admin" interface within the Appmixer Studio and proceed to the "Users" page. Please note, you must possess admin user privileges to access the "Admin" interface:
After locating the flow you intend to troubleshoot, proceed to examine the flow's logs and configuration. Click on the "Logs" link to view the flow's logs, and utilize the "Flow" link to inspect the flow's configuration in the Appmixer Designer.
As an Appmixer admin user, you have the capability to directly make minor adjustments to a flow from within the designer. However, it's important to recognize the boundaries of such modifications. For instance, re-authenticating to the applications within the flow is not possible, as authentication was initially performed by the end-user. Additionally, altering the flow's structure is discouraged, as it may necessitate new inputs from the end-user. Focus on implementing small, necessary corrections, and guide the end-user to undertake more significant changes by re-configuring their integrations according to your instructions.
In the "Users" section, you'll find a comprehensive list of all users within the system. The interface allows you to search for users based on multiple criteria, with email being the most common search parameter. Upon locating a user, you can click on the Flows icon () to view all the flows associated with that user, including integration instances and automations.
This tutorial will guide you through the process of creating, testing, and publishing your first integration. Specifically, you will learn how to:
Build an integration template using the Appmixer no-code Studio.
Parametrize your template by adding fields to gather information from your end-users through an easy-to-use web form (Wizard).
Validate your integration's functionality through testing.
Publish your integration to your end-users for immediate use.
Explore a demo of how your integrated marketplace could be presented to your end-users.
In our tutorial, we will demonstrate an integration that processes newly created contacts as external events. This simulates a real-world scenario where you would send these events via HTTP requests from your application code. The integration examines each contact for the presence of a hotLead
parameter. If this parameter is set to true
, the contact is then forwarded to Slack.
On the Integrations page, click the "Create Integration" button located in the top right corner. Then, select the OnAppEvent
trigger from the Trigger selector. Utilizing App Events represents the simplest method for sending data to Appmixer.
Configure the OnAppEvent
trigger by setting the Event name to contact-created.
Use { "first": "John", "last": "Doe", "hotLead": true }
as the Event Data Example. This data sample will enable us to reference the specific data fields later in the integration workflow.
Next, add the Condition
component to your flow. Do this by dragging the Controls
module from the left palette to the canvas. Then, select the Condition
options from the Action panel on the right:
Configure the Condition
component to check whether the hotLead
property from the OnAppEvent
trigger is set to true
. It's important to note that you can utilize the "+" button adjacent to any configuration field. This feature allows you to reference data originating from any component earlier in the integration flow, regardless of its depth in the workflow.
Next, drag the Slack
module from the left palette to the canvas. Choose the SendChannelMessage
action and connect it to the true
output port of the Condition
component:
Our integration template is nearly complete. The next step involves configuring the Slack.SendChannelMessage
component. You may have noticed that this component includes three configuration fields: Slack account, Slack channel, and Message. Importantly, these fields should not be hardcoded by the template creator. Instead, they are intended to be customizable by the end-user. This means the end-user should have the ability to authenticate with their own Slack account, choose their desired Slack channel, and tailor the message to be sent.
You can enhance user convenience by setting default values for any field, facilitating quicker integration activation for your end-users. To do this, simply enter a value into the desired field. For example, we'll set a default value for the "Slack message" field:
Additionally, you have the option to apply data transformations to any data variable incorporated into a field. To do this, click on the desired variable, which will open the Modifiers panel. Here, you can define a sequence of modifiers that will be applied in the specified order once the integration is activated and the data becomes available. For our scenario, we aim to transform the last name of the incoming contact to uppercase.
Finally, it's important to assign a meaningful name to our integration:
At this point, our integration is prepared for publishing. However, before proceeding with that, it's crucial to verify that the integration functions as anticipated. Initially, we should examine the appearance of the final configuration web form (Wizard) as it will be presented to our end-users. Additionally, for improved clarity, we recommend renaming the default "Channel" and "Message" fields within the wizard to "Slack Channel" and "Slack Message," respectively. To do this, click on the "Edit Wizard" button located at the top of the interface. This will allow you to view a live preview of the wizard and make adjustments to the fields via the left navigation panel.
Next, proceed with a live test of the integration. Click the "Start Test" button at the top of the page to initiate the integration within your user context (as the template creator). This action will launch the Wizard in its defined state, allowing you to configure the necessary fields. Once the test integration is activated, you'll have the opportunity to experience the integration's functionality firsthand, just as an end-user would.
With our test currently running, you have the ability to manually initiate the OnAppEvent trigger. This is achieved by sending a custom App event to the active test integration. To do this, click the "Test" button located at the top of the interface, which will open the Test menu. From here, choose "Send App Event." For the Event name, enter "contact-created
," and for the Event Data, use an example such as { "first": "John", "last": "Smith", "hotLead": true }
. After submitting the event, monitor your Slack channel to verify that the contact information has been successfully posted.
Additionally, you can view the activity of your running test integration by navigating to "Test" and then selecting "Insights" from the menu:
Now that your integration is ready and has been thoroughly tested, you are all set to publish it for your end-users.
Please note, if Appmixer has not yet been embedded into your product, you can utilize our demo web app. This allows you to preview how your embedded integration marketplace will appear in your app once the actual embedding is completed.
This tutorial is designed to walk you through the process of building your first automation. Whereas integrations consist of predefined workflows that end-users can easily activate via a web form, automations are a powerful tool for enhancing your internal business operations. They allow you to link applications and services used within your organization or to launch bespoke business logic that meets your unique requirements.
In this tutorial, we'll construct an automation designed to gather customer feedback via Typeform, relay each piece of feedback as a Slack notification, and automatically generate a GitHub issue based on the customer's response to the question: "What improvements or additional features would you like to see in future updates?". Utilizing OpenAI's ChatGPT, the generated ticket will be structured as a comprehensive user story, encompassing title, role, goal, reason, acceptance criteria, and test case.
Let's delve into the specifics of the automation flow:
Here's the first piece of customer feedback collected through our Typeform:
As a consequence, we received a notification in Slack:
Furthermore, a new GitHub Issue was created containing this content:
The upcoming sections will walk you through the steps required to build the aforementioned automation from start to finish.
Every automation begins with a trigger, which initiates the automation flow. Typically, triggers can capture data from external sources—like a Webhook trigger or a google.email.NewEmail
trigger—or they can be based on time events, such as a Scheduler or Timer. This setup allows you to program your automations to run at predetermined times, for instance, "every day" or "each week on Monday at 2pm". A unique trigger type is the "OnStart" trigger, which activates immediately when your automation flow is started. This is particularly useful for batch operations that you intend to run only once or for debugging purposes.
To start building an automation, go to the Automations page and select "Create Automation". This will lead you to the Automation Designer. The first item you'll encounter there is the Trigger Selector. Look up "Typeform" and choose the "NewEntry" trigger.
In the Configuration Inspector on the right, authenticate with Typeform and select one of your forms:
Drag the Slack module from the left panel and choose the "SendChannelMessage" action in the Inspector panel on the right. Authenticate with your Slack account and select a Slack channel to direct your notifications:
Click the "+" button next to the Slack message field to include data placeholders for each Typeform question. These placeholders will be substituted with real data once the automation executes and retrieves customer feedback from Typeform:
Similarly, add the "OpenAI.CreateChatCompletion" action. Configure the model, set the response type to json_object
, and define the prompt as follows:
The goal is to have ChatGPT generate a JSON comprising two fields: "title" and "content." This JSON will then be seamlessly integrated into the "Github.CreateIssue" action. The structure of our output JSON should resemble the following:
When your automation flow includes a component that outputs data you need to alter and then utilize the modified data across various configuration fields, employing the Control.SetVariable component proves beneficial. This component enables you to define custom variables, which can subsequently be referenced by name in other linked components. This approach is particularly advantageous when you aim to use transformed data in several locations, as it obviates the need to replicate the same modifications for a single variable across multiple fields.
In our scenario, we intend to capture the output of the ChatGPT completion (ChatGPT's response) in a variable named result
. This allows us to conveniently reuse this data for both the title and description fields when we're ready to create our GitHub issue.
Given our interest in only the first choice, and considering that the message content of this choice is a JSON string (as specified in our prompt to ChatGPT), we need to manipulate the Choices variable to extract our desired JSON. To transform data within Appmixer, simply click on a variable to access the Modifiers panel and then sequentially apply the necessary modifiers, akin to how formulas are utilized in an Excel sheet. In our case, we will employ the sequence of modifiers: First Item -> JSON Path -> Parse. This sequence will allow us to select the first item from the Choices list, extract the message.content
from this item, and finally parse the message.content
into a JSON object for later use. It is crucial to note that ChatGPT outputs a JSON string, not a JSON object, necessitating the Parse modifier to convert the text into structured data.
The final step involves creating a GitHub Issue using the title and content (in Markdown) generated by our AI. Navigate through the interface to locate the GitHub connector, then drag and drop it onto the canvas. Select the "CreateIssue" action and assign response.title
and response.content
(the JSON fields generated by ChatGPT) to the corresponding fields within the "Github.CreateIssue" action. As an additional step, choose the "enhancement" label from the selection box to categorize our GitHub issues as enhancements.
Your automation is now set up and ready to be launched. Begin by giving it a distinctive name; this can be done by double-clicking on the default "New flow" title and renaming it to something more descriptive, like "Track Customer Feature Requests". To activate your automation, click the "Start flow" button located in the top right corner. You'll notice the log panel opens at the bottom displaying the initial log entry: "Flow started". As customer feedback starts coming in through our Typeform web form, the log panel will populate with entries detailing the actions taken within the flow. These logs provide insights into each component's input, the data transmitted between components, and the output generated, offering a comprehensive view of the automation's operation:
Clicking any log entry will reveal a panel that offers additional details.
Additionally, you can visit the Insights page to access enhanced filtering options and gain improved visibility into your logs. For instance, if you're specifically interested in logs pertaining to the CreateIssue component, you can utilize the Flows selector in the left panel. This allows you to select and view logs exclusively for the component of interest.
This action will produce a filtered list of logs:
Additionally, the Time range picker allows you to choose any date interval you're interested in for viewing the logs.
Lastly, you can search for specific strings using the Search filter to find particular entries in the logs.
Appmixer enables you to expand its default set of connectors with your own custom connectors. These connectors can interface with third-party APIs, your internal APIs, or provide utilities or business logic tailored to your product.
In Appmixer, connectors are implemented as Node.js modules, accompanied by JSON manifest files. These files contain metadata such as icons, descriptions, and input/output definitions.
Explore the public , which houses almost all Appmixer connectors, for inspiration for your own connectors. Additionally, you're invited to contribute by submitting a pull request (PR) to the repository. This collaborative approach allows you to not only benefit from the collective knowledge of the Appmixer community but also to share your innovations with others.
In this guide, we will demonstrate how to implement a basic connector, offering just one action component. We will create a connector for the Bored API, a public API that requires no authentication and provides a single HTTP GET endpoint. This endpoint returns a random activity suggestion for when you're feeling bored.
Our component will feature one input port, allowing it to connect with other components within your automations and integrations. It will also have one output port for linking to subsequent actions that might utilize the data from our GetActivity component. Additionally, our component will offer an option for users to specify the type of activity they wish to receive—be it "education," "recreational," or "cooking."
Please note that YOURTENANT
refers to the tenant ID assigned to you during the signup process.
At this point, your CLI is initialized, and you are ready to start building, testing, and publishing your components.
To generate a boilerplate code for our connector, we will use the Appmixer CLI's generator tool:
In our example above, we used the tool to generate a component with the ID appmixer.boredapi.core.GetActivity
. It's important to adhere to Appmixer's convention for fully qualified component names, which follows the pattern [VENDOR].[SERVICE].[MODULE].[COMPONENT]
. The description is provided using the --description
parameter. The author of the component is specified with the --author
parameter, while --inPorts
and --outPorts
parameters are used to define comma-separated names of input and output ports, respectively (in this case, just one port for each type). The --iconUri
parameter allows the passing of a URL for an icon that will be used for the component. Note also that the generator does more than just output the single component code and manifest file; it creates the necessary directory hierarchy and the service.json
connector manifest file to complete our connector.
The resulting file structure should resemble the following:
Each component is comprised of two main files: a component.json
manifest file that defines the metadata of the component, and a Node.js module (GetActivity.js
in our case) that implements the behavior of our component, namely, how it processes inputs and produces outputs. In this guide, we will leave the package.json
file as is, since our component does not require any dependencies (no third-party Node.js libraries are needed).
Simultaneously, we will define the output of our component, which corresponds to the response received from the HTTP GET request to the /api/activity
endpoint. While defining the output is optional, omitting it means users utilizing our component in their automations cannot reference specific values directly when connecting our component's data with other components.
Originally, our generated component.json
manifest file looked like this:
After incorporating our changes, which include adding a type
input parameter and defining the output, the manifest file will be updated as follows:
The final piece of our component is its behavior—specifically, how it responds to inputs and generates outputs. This is achieved through a Node.js module that exports functions recognized by Appmixer. The most crucial function is the receive(context)
method, which Appmixer triggers whenever the component receives an input. Within this method, you can implement any business logic or make calls to third-party or internal HTTP endpoints (other protocols are also supported, essentially anything that Node.js can handle). When your component is prepared to output data (typically by using the response object from an HTTP call), it should use the built-in context.sendJson(object, PORT)
function. The final GetActivity.js
file will appear as follows:
The receive()
function initiates an HTTP GET request to our API, incorporating the type
input as a query parameter with the same name. All user inputs are accessible through the context.messages.[INPUT_PORT_NAME].content
object. We also utilize the built-in context.httpRequest()
method to simplify the initiation of HTTP requests. While you're free to use any Node.js library of your choice for making HTTP requests, the httpRequest()
method offers a convenient alternative that includes easy request sending and proper error logging.
It's important to note that our code does not explicitly handle errors. This is because Appmixer is designed to automatically manage errors on behalf of the component. If the receive()
function encounters an error, such as an HTTP request failing, Appmixer will retry the function automatically at a later time, employing an exponential backoff strategy.
Although your component is now ready to be published to your Appmixer tenant, it is advisable to first conduct local testing. Continuously updating your component code, republishing it, and reconfiguring or restarting your test flow for every test can be cumbersome. Consequently, the Appmixer CLI includes a tool that facilitates local testing of your component. In our scenario, we aim to test our component by providing the type
input, allowing the component to execute its action (make an HTTP request), and then observe the output:
Below is the console output from our test:
Now, we are ready to publish the component to our Appmixer tenant. This will enable us to utilize it in our integration templates, internal automations, or make it available to our end-users through the embedded automation designer.
Finally, we can set up an automation that sends us an email every day at 17:00, providing ideas for cooking activities:
If you need to make changes to your connector, simply edit the files, re-pack, and re-publish. Your changes will then be reflected in your tenant.
To allow end-users to configure fields, click on the magic wand button next to each field. This will add the fields to the Configuration Wizard for end-user customization:
First, you need to install the tool, which enables you to build, test and deploy custom connectors. You can install the tool using npm
:
Now, you need to initialize the CLI tool to configure it for your Appmixer tenant and log in with an admin account. Before you publish your components, verify that your admin account is To do this, visit your Appmixer tenant's backoffice at https://backoffice.[YOURTENANT].appmixer.cloud
. Navigate to the Users page, locate your user account, edit it, and add "appmixer" as the vendor. Without having the vendor assigned, you will not be able to publish components to your Appmixer tenant.
We can now proceed to edit the component.json
manifest file, adding an input field to allow users to specify the type of random activity they wish to receive. This type
field corresponds directly to the type
query parameter of the .
The schema.properties
section defines a for our input while the inspector.inputs
section to collect input from the user.
Appmixer offers a REST API that mirrors the functionalities accessible through its UI. This API enables advanced customizations, additional automations, and scripting capabilities for enhanced integration flexibility.
The base URL for the Appmixer REST API varies according to your specific Appmixer tenant. The URL format is as follows:
The majority of the Appmixer API endpoints require authentication using an access token associated with an Appmixer user account. Certain endpoints may require that this user to has the admin
scope; specific requirements are detailed at each endpoint's documentation. To obtain a user's access token, sign in using the user's credentials:
On success, Appmixer returns a JSON object structured as follows:
Use the token
in the Authorization
header to authenticate your user to the Appmixer API. For example:
Find the full documentation to the REST API here.
Please visit the Appmixer Knowledge base for end-user tutorials.
This set of endpoints control the connectors that are installed in your Appmixer tenant. You can also publish new and uninstall existing connectors.
GET
https://api.YOUR_TENANT.appmixer.cloud/apps
Returns all applications (services or modules) available.
curl "https://api.appmixer.com/apps" -H "Authorization: Bearer [ACCESS_TOKEN]"
GET
https://api.YOUR_TENANT.appmixer.cloud/apps/components
Returns all components of an app including their manifest files.
curl "https://api.appmixer.com/apps/components?app=appmixer.dropbox" -H "Authorization: Bearer [ACCESS_TOKEN]"
GET
https://api.YOUR_TENANT.appmixer.cloud/components
Get all available components.
curl "https://api.appmixer.com/components" -H "Authorization: Bearer [ACCESS_TOKEN]"
POST
https://api.YOUR_TENANT.appmixer.cloud/components
Publish a new component, new module or an entire service or update an existing component/module/service. The uploaded entity must be zipped and must have a proper directory/file structure inside (See Basic Component Structure for more details). Note that you can use the Appmixer CLI tool to pack your component/service/module to a zip archive (appmixer pack
command). Alternatively, you can also use a regular zip command line utility but you should omit the node_modules/ directories before archiving and the root directory of the zip archive must match the vendor name. The directory hierarchy must have a well defined structure: [vendor]/[service]/[module]/[component]
.
The size limit of the zip archive is 10MB.
Publishing a new component can take more time than a lifespan of a single HTTP request. Therefore, the result of the publishing HTTP call is a JSON object with ticket
property. You can use the ticket to check for progress of the publish using the /components/uploader/:ticket
endpoint.
curl -XPOST "https://api.appmixer.com/components" -H "Authorization: Bearer [ACCESS_TOKEN]" -H "Content-type: application/octet-stream" --data-binary @appmixer.myservice.zip
GET
https://api.YOUR_TENANT.appmixer.cloud/components/uploader/:ticket
Check for progress of publishing a component.
curl -XGET "https://api.appmixer.com/components/uploader/2e9dd726-2b7f-46f7-bea4-8db7f7175aa8" -H "Authorization: Bearer [ACCESS_TOKEN]" -H "Content-type: application/json"
DELETE
https://api.YOUR_TENANT.appmixer.cloud/components/:selector
Delete a component/module or an entire service.
curl -XDELETE "https://api.appmixer.com/components/appmixer.myservice" -H "Authorization: Bearer [ACCESS_TOKEN]" -H "Content-type: application/json"
Appmixer includes a variety of ready-to-use connectors, with some being immediately operational while others necessitate user authentication with third-party systems (such as Slack, Gmail, Teams, and many others). This authentication process typically employs the OAuth 2 protocol.
Appmixer features an internal Authentication Hub, functioning as an authentication proxy, which simplifies the setup process by eliminating the need to register your own OAuth credentials with third-party services. This means all OAuth-based connectors offered by Appmixer are ready to use right out of the box. However, should you prefer to use your own OAuth credentials for enhanced customization or compliance reasons, you have the flexibility to do so. This can be done for all or selected connectors by specifying your OAuth credentials (clientId
and clientSecret
) as outlined below.
The Connector Configuration is available via the Appmixer Backoffice interface:
For detailed information on configuring specific connectors, please consult the App Registration section of our documentation. This section will guide you through the necessary steps and provide insights on setting up each connector according to your needs.
Appmixer offers several methods for configuring the OAuth credentials, with the Appmixer Backoffice being the simplest. For instance, if you're looking to use Slack components, you'll first need to register your application on the Slack developer portal, where you'll receive a clientId and clientSecret. After obtaining these, you can save them into Appmixer as follows:
For the Configuration ID, use appmixer:slack
. You are now ready to add the Slack OAuth app clientId
and clientSecret
.
Add a key named clientId
(it's crucial to use exactly clientId
, not clientID
or any other variation) and assign it the clientId
you received from Slack.
Following that, add the clientSecret
you received from Slack.
Once you've added the clientId
and clientSecret
, you're all set to utilize the Slack components within Appmixer.
It's possible to include any key/value pairs in this configuration. These pairs will be accessible within your component's code via the context.config
object (or context.auth
), and in the case of auth.js
files, directly within the context
object.
This method proves particularly beneficial for API key-based authentications in your custom connectors when you prefer not to present your end-users with a form to enter their own API key. Instead, opting to use a single, global API key for all users allows them to freely use the connector without the necessity for individual authentication. This approach enhances user convenience by simplifying access and usage of the connector, ensuring a seamless experience without compromising on security or functionality.
A prime example is the DeepAI component. For utilizing the Deep AI API, an API key is required. However, instead of having your users supply this API key individually, you might prefer to use a single API key across all users for simplicity and ease of management. This setup allows for a more streamlined user experience, with further details available in the documentation.
Additionally, for components using OAuth2 authentication, you have the option to redefine the default callbackUrl
(redirect URL). This flexibility allows you to customize the authentication process to better fit your application's workflow.
In scenarios where third-party developer consoles require the verification of domain ownership for domains used as callback URLs in OAuth-registered applications, you might face challenges (for example with configurations like the Google OAuth app). Given that your Appmixer tenant operates under a domain such as api.YOUR_TENANT.appmixer.cloud
and you lack access to this domain's DNS settings, your options for domain verification can seem limited.
To facilitate this process, the Appmixer Backoffice includes a tool designed specifically for domain verification purposes. This tool enables you to upload a file that the third-party service has generated for verification. Once uploaded, this file becomes accessible at the root of your domain. The third-party service verifies domain ownership by checking for the presence of this file at the specified root location on your domain, thus confirming that you indeed control the domain. This public file method provides a straightforward solution for verifying your Appmixer tenant domain without the need for DNS access.
To upload a public file to the root location, navigate to the Appmixer Backoffice interface and select the "System -> Public Files" option from the left menu. On this page, you'll find the functionality to upload your files.
Once you've uploaded your files through the Appmixer Backoffice, they will be accessible via your Appmixer Tenant API URL. For instance, if you upload a file named google52658022a92d779c.html
, it can be accessed at https://api.YOUR_TENANT.appmixer.cloud/google52658022a92d779c.html
as well as at the well-known directory URL https://api.YOUR_TENANT.appmixer.cloud/.well-known/google52658022a92d779c.html
. This accessibility ensures that third-party services can easily verify domain ownership by locating the specified file at your domain's root or the well-known directory.
For customers managing their own Appmixer installations, there's flexibility to adjust where these public files are hosted through the PUBLIC_FILES_PREFIX
system configuration option. This setting allows you to tailor the file access paths to suit your infrastructure requirements, providing further control over how you manage domain verification and public file accessibility.
App Events provide the simplest method for sending data to Appmixer. To utilize App Events, select the OnAppEvent trigger from the Utilities category of connectors:
Note that another convenient way to trigger your automations is by using the Webhook
component from the HTTP module. However, the Webhook
component may not be as effective for building integration templates. This is because when integrations are activated by end-users, the Webhook component generates a new URL each time since each activated integration is a new instance derived from the template. Consequently, the Webhook URL displayed in the inspector panel (configuration panel) during template creation will not match the URLs in the actual integration instances.
In contrast, the OnAppEvent
trigger creates a named webhook, allowing you to target a specific user with specific events and data by calling it with the actual end-user access token.
In summary, if you only care about automations where the webhook URL is known, you can use Webhook
. If you build integration templates, use OnAppEvent
instead.
Assign a meaningful name to your app event and consider providing sample data. Although including sample data is optional, it facilitates referencing the data from the app event in subsequent components.
Providing an Event Data Example populates all detected JSON fields as variables in subsequent connected components. This allows you to easily use and reference these data placeholders throughout your integration, ensuring that the necessary information is accessible and can be dynamically incorporated into various parts of your workflow. This practice enhances the configurability and functionality of your automations by clearly mapping out how data flows between components.
Once your automation or integration templates are built, published, and users begin activating your integrations, you can trigger app events by using the appmixer.api.sendAppEvent(EVENT, DATA)
function provided by the client-side Appmixer SDK. This function enables you to programmatically send events and associated data through your application, facilitating real-time interaction and response within your integrations.
This triggers all the integrations or automations associated with the user who is authenticated with the access token used in the SDK, specifically targeting those that begin with the OnAppEvent configured with the Event Name set to contact-created
.
Alternatively, you can trigger the OnAppEvent from either client-side or backend-side code by sending an HTTP POST request to the endpoint https://YOUR_APPMIXER_TENANT_API_URL/plugins/appmixer/utils/appevents/events/EVENT
. When making this request, include the event data in the payload. This method allows you to directly interact with the Appmixer system via HTTP, providing flexibility to trigger events from various parts of your application infrastructure.
If you don't have the access token for a virtual user, you can obtain it by using their username and password to call the Sign-in endpoint. This process involves submitting the necessary credentials to authenticate the user, after which the endpoint will provide an access token. This token can then be used to authorize subsequent actions or API calls under that user's identity.
When errors arise in running automations or integrations, Appmixer does not automatically send emails to your end-users. Instead, it offers complete customization of communication with your end-users, enabling you to send your own branded emails or other types of notifications. This approach allows you to maintain consistency in your communication strategy and ensure that all messages align with your brand identity.
It's worth noting that you can set up your own Appmixer automation to manage error notifications. Simply utilize the Webhook trigger as the starting point for your automation and register the Webhook URL with the WEBHOOK_FLOW_COMPONENT_ERROR
System Webhook configuration in the Backoffice. This method allows you to automate the notification process effectively, tailoring it to meet your specific needs and and avoid implementing a new endpoint in your own backend application.
To manage errors within running integrations or automations, configure the WEBHOOK_FLOW_COMPONENT_ERROR
system variable with a custom URL in the Appmixer Backoffice interface. Appmixer will then send an HTTP POST request to this URL each time an error occurs in a flow, enabling you to respond or notify as necessary based on the error details provided. This setup provides a streamlined way to monitor and address issues in real-time.
The payload that Appmixer sends to the registered URL when an error occurs in a flow has the following structure:
While the error message from Appmixer provides comprehensive details about the issue, it's important to communicate this information to your end-users in a more digestible format. To ensure your notifications are user-friendly and avoid overwhelming your end-users with technical jargon, we recommend structuring your communications as follows:
Given a scenario where the SendEmail
component in a flow mistakenly references the Start time output of the OnStart
trigger instead of an email address in the To field, a runtime error will occur. This mistake results in the flow attempting to use a timestamp as the recipient's email address, which is invalid. Consequently, this misconfiguration triggers a runtime error, which is then accurately recorded and can be reviewed in the log viewer.
When an unrecoverable error occurs, such as the one described with the SendEmail component misconfiguration, Appmixer will package the error details into a JSON object. This JSON payload is then sent via an HTTP POST request to the URL specified in the WEBHOOK_FLOW_COMPONENT_ERROR
system variable. The structure of this payload is designed to provide comprehensive information about the error, including the component that caused it, the nature of the error, and any relevant data to identify and address the issue:
If the flow is configured correctly with a valid email address but encounters a network error preventing Appmixer from sending the email, this situation represents a temporary or external issue rather than a misconfiguration within the flow itself. In such cases, Appmixer will attempt to handle the error based on its retry policies or error-handling mechanisms. If the error persists and is deemed unrecoverable for that instance of execution, Appmixer may report this failure through the WEBHOOK_FLOW_COMPONENT_ERROR
system variable, if configured. The notification sent to the specified webhook URL will detail the error, indicating it was a network issue affecting the email send action, allowing for appropriate troubleshooting or user notification.
Users can then review this error within the log viewer or under Insights, where all flow activities and errors are documented for analysis.
Appmixer sends out a JSON payload to the registered URL containing detailed information about the error:
It's important to note that Appmixer employs an automatic retry mechanism for handling network errors when sending messages. This involves multiple attempts to resend the message, following an exponential backoff strategy to optimize the timing of retries. Thus, the error notification sent via the WEBHOOK_FLOW_COMPONENT_ERROR
system variable, along with the detailed JSON payload to the registered URL, occurs only if all retry attempts fail. This approach ensures that temporary issues have ample opportunity to be resolved before escalating the error, aiming to maintain the reliability of your automations and integrations without immediate interruption from transient network issues.
Manage the modules available in the system.
The "Connectors" section of the Appmixer Backoffice interface shows all available connectors that you can manage, including options to install, update, or remove them. Connectors that are already installed will display an "Installed" badge at the top. Furthermore, you have the ability to search for connectors based on various criteria, such as their name or installation status, to easily find the ones you're interested in managing.
Clicking on any connector will redirect you to that connector's detailed page, where you can find more information and manage settings specific to that connector.
On the connector's detail page, you will find a comprehensive description of the connector, along with options to download the connector as a zip file, and to install, update, or remove the connector from your Appmixer tenant. Additionally, the page provides a list of the connector's components, including actions and triggers, complete with their respective descriptions. This information aids in understanding the functionality and potential use cases of each component within your workflows.
To install new connectors, simply click the "Install Connector" button found on the Connector details page.
After installing a connector, it's advisable to consult the connector's documentation to determine if additional configuration is necessary. Appmixer simplifies OAuth configuration by automatically handling it, unless specified otherwise in the connector's configuration. This is achieved through the internal Authentication Hub proxy, which utilizes Appmixer's own OAuth credentials for each connector to authenticate your end-users remotely via this proxy. This approach streamlines the connector installation process, eliminating the need to register your own OAuth applications with each service provider. However, if you prefer to use your own OAuth credentials, you should refer to the "Custom OAuth Credentials" section of the documentation for guidance.
By default, all users within your Appmixer tenant instantly gain access to any installed connectors. If you prefer to modify this default setting, you can do so by removing the user * *
ACL rule from the ACL configuration. To access this option, navigate to System -> ACL from the left menu. Removing this rule will revoke all users' access to the connectors. Subsequently, you can utilize the "Add User Rules for Installed Connectors" button, which automatically establishes specific access rules for all installed connectors. This method is particularly useful because when you install a new connector in the future, it won't be immediately available to your end-users. This delay provides you, the admin user, with ample opportunity to properly configure and test the new connector before making it accessible to your end-users.
For enhanced clarity, the details page of each connector includes an "Access Control" section. This section outlines all the ACL (Access Control List) rules applicable to that specific connector, helping you understand who has access to it. To view a comprehensive list of all Access Control rules across connectors, you can navigate to the System -> ACL page via the left menu. This centralized view facilitates easier management and oversight of permissions and access within your Appmixer tenant.
You have the ability to uninstall any connector from your Appmixer tenant by clicking the "Remove Connector" button. This action will uninstall the connector and remove it from your Appmixer environment.
However, caution is advised when removing connectors. Any flows (whether integrations or automations) that include components from the connector you intend to remove will cease functioning. It's important to ensure that any such flows are halted before proceeding with the removal. The most straightforward method to verify this is to visit the Flows page in the Backoffice, applying filters by the connector name and running status, to identify and stop any active flows utilizing the connector in question.:
This process enables you to individually navigate to the Designer for each flow by clicking on the "Flows" link in the right menu for each listed flow, allowing you then to stop the flow directly from the Designer interface.
For more extensive connector cleanups or when manual management of flows is not feasible, you can leverage the Appmixer REST API. This allows you to retrieve flows using the connector with a GET request to https://api.YOUR_TENANT.appmixer.cloud/stats/component-usage?componentType=trello&stage=stopped&group=$flowId
, and subsequently stop each flow individually with a POST request to https://api.YOUR_TENANT.appmixer.cloud/flows/:FLOW_ID
, including a JSON body with { "command": "stop" }
and setting "Content-Type": "application/json"
in the header.
Failing to stop flows that depend on a connector before its removal will lead to errors in the running flows when the connector components are triggered. These errors will be reported to the System Webhook, to which you can subscribe for notifications. The impact of such failures varies by component type:
Trigger Components: These might continue to poll and fail. However, if the connector is re-installed, they can recover, allowing the flow to resume operation.
Action Components: These will fail to receive messages from other connected components. After a set period and a number of retries following an exponential backoff strategy, the message is discarded and moved to the dead-letter queue, accessible via the Unprocessed Messages API. Should the connector be re-installed before exhausting all retry attempts, the action component can recover and will continue to process messages.
When creating custom connectors for your Appmixer tenant, there are three methods available for uploading them. First, you can utilize the Appmixer CLI to package and publish your connectors; for detailed instructions, refer to the "Build a Custom Connector" section. Alternatively, you can publish your connector as a zip archive through the Appmixer REST API; for this method, consult the "Apps endpoints" documentation. Lastly, if you prefer a direct approach, you can manually upload the connector via the Backoffice by clicking the "Add Connector from your Filesystem" button located at the bottom of the Connectors page. Each method provides a convenient way to integrate your custom connectors into Appmixer, allowing you to choose the best approach based on your preferences and needs.
Using the Appmixer CLI to build, test, and publish your connectors is highly recommended as it offers the most control and flexibility. However, if you're looking to quickly test a connector provided by the Appmixer team or a colleague, the UI option to upload your connector can be a convenient alternative. This approach is particularly useful for expedited testing or evaluation of connectors without the need for extensive setup.
The Appmixer team diligently focuses on enhancing connectors and ensuring they remain compatible with updates to third-party APIs. Whenever new versions of connectors become available, all Appmixer customers are notified, with the notification including a list of both newly added and updated connectors. To explore these updates, you can visit the Connectors page in the Backoffice and use the "update status" filter. This will display a list of connectors that have updates ready for installation. This proactive approach helps maintain the efficiency and reliability of your integrations, keeping them aligned with the latest API changes and functionalities.
By visiting the Connector Details page, you have the option to update the connector by clicking the "Update Connector" button. For insights into what changes the update includes, such as fixes or improvements, refer to the Changelog section. This section provides detailed information on the modifications made in the latest version of the connector, helping you understand the enhancements or corrections applied.
Most updates to connectors are designed to be non-disruptive and do not introduce breaking changes, ensuring a smooth transition to the newer version. However, there are instances where updates may necessitate breaking changes. These changes could be required to enhance security, improve functionality, or comply with modifications in third-party APIs. In such cases, these updates are essential for the continued effectiveness and reliability of the connector, even though they may require adjustments in how the connector is used within your workflows.
Connectors in Appmixer adhere to the Semantic Versioning Schema, which is structured as MAJOR.MINOR.PATCH
. Updates that involve changes to the MINOR
or PATCH
version numbers generally do not impact existing integrations or automations, allowing for seamless updates. However, when there is a change in the MAJOR
version number, it indicates that a breaking change has been introduced. Such changes can affect running integrations and automations that utilize the connector.
When connectors and their components undergo significant upgrades, such as radical changes to third-party service API endpoints, shifts from API key authentication to OAuth, or the introduction of new required fields in API endpoints, a major version upgrade of the connector might be necessary. These upgrades often render the connector incompatible with flows created using its older version. Consequently, after upgrading to a new major version of a connector, existing flows cannot be automatically updated to accommodate the changes, as such modifications typically require action from the flow's owner. This might involve re-authenticating due to changes in the authentication protocol or configuring new required fields, among other adjustments.
Should you attempt to update a connector that includes such a breaking change, a confirmation dialog will appear, cautioning you about the introduced breaking change. It will also notify you that all active flows (both integrations and automations) will be automatically stopped to prevent them from malfunctioning due to the incompatibility with the upgraded connector version.
Upon confirming the update, a progress indicator will be displayed, informing you that the flows are in the process of being stopped.
Before proceeding with connector upgrades, it's recommended to configure the System Webhook WEBHOOK_FLOW_STOPPED
to point to a URL endpoint within your own application or to a Webhook within an Appmixer automation designed specifically for this scenario. This setup is intended to notify end-users (i.e., the owners of the integration instance or automation) that their integrations require reconfiguration due to the update.
The payload of this webhook will contain relevant information to inform the user about the flow that has been stopped and potentially why it was stopped, providing a clear indication that action is needed on their part to update and restart their integrations. This proactive communication ensures users are aware of any necessary adjustments to maintain the functionality of their integrations after major updates.
API for users
POST
https://api.YOUR_TENANT.appmixer.cloud/user/auth
Sign-in a user with credentials and get their access token.
curl -XPOST "https://api.appmixer.com/user/auth" -H "Content-type: application/json" -d '{ "username": "abc@example.com", "password": "abc321" }'
POST
https://api.YOUR_TENANT.appmixer.cloud/user
GET
https://api.YOUR_TENANT.appmixer.cloud/user
Get current user information.
curl "https://api.appmixer.com/user" -H "Authorization: Bearer [ACCESS_TOKEN]"
GET
https://api.YOUR_TENANT.appmixer.cloud/users/:userId
Admin token required.
GET
https://api.YOUR_TENANT.appmixer.cloud/users
Admin token required.
Examples:
Get the first 30 users with a scope "acme1":
curl -XGET "https://api.appmixer.com/users?filter=scope:acme1&sort=created:-1&limit=30&offset=0" -H 'Authorization: Bearer [ADMIN_TOKEN]'
Get all users who's username includes a pattern:
curl -XGET "https://api.appmixer.com/users?pattern=joe" -H 'Authorization: Beader [ADMIN_TOKEN]'
GET
https://api.YOUR_TENANT.appmixer.cloud/users/count
Admin token required
PUT
https://api.YOUR_TENANT.appmixer.cloud/users/:userId
Admin token required.
DELETE
https://api.YOUR_TENANT.appmixer.cloud/users/:userId
Admin token required. This operation stops all running flows and deletes all the user's data from the system - logs, accounts, tokens ... The response is a ticket, the operation may take a long time. You can use the ticket and poll for the result with the next API method.
GET
https://api.YOUR_TENANT.appmixer.cloud/users/:userId/delete-status/:ticket
POST
https://api.YOUR_TENANT.appmixer.cloud/user/change-password
User token required.
POST
https://api.YOUR_TENANT.appmixer.cloud/user/reset-password
Admin token required.
POST
https://api.YOUR_TENANT.appmixer.cloudforgot-password
POST
https://api.YOUR_TENANT.appmixer.cloud/forgot-password/reset
Reset user password by providing unique code generated via POST /user/forgot-password
API.
The vast majority of API endpoints within the Appmixer require an access token to execute the calls. The following methods explain how to create a user and obtain the access token through the sign-in endpoint.
POST
https://api.YOUR_TENANT.appmixer.cloud/user/auth
Sign in a user with credentials and get their access token.
curl -XPOST "https://api.appmixer.com/user/auth" -H "Content-type: application/json" -d '{ "username": "abc@example.com", "password": "abc321" }'
You can sign in either with your username
and password
or with your email
and password
.
POST
https://api.YOUR_TENANT.appmixer.cloud/user
GET
https://api.YOUR_TENANT.appmixer.cloud/user
Get user information.
curl "https://api.appmixer.com/user" -H "Authorization: Bearer [ACCESS_TOKEN]"
Get list of all messages passing through your flows and usage information (telemetry).
GET
https://api.YOUR_TENANT.appmixer.cloud/logs
Get logs for a single flow or list of flows or all user's flows. Filtering and sorting supported. Logs contain data messages getting into the component's input port(s) and messages sent to the component's output port(s). They also contain any errors that occurred during the flow run or while trying to start/stop a flow.
curl "https://api.appmixer.com/logs?from=0&size=30&sort=gridTimestamp:desc&query=gridTimestamp:[2019-03-04+TO+2019-03-08]&flowId=9c4673d7-a550-45a2-91c1-ad057fac0385" -H "Authorization: Bearer [ACCESS_TOKEN]"
curl "https://api.appmixer.com/logs?from=0&size=30&sort=gridTimestamp:desc&query=gridTimestamp:[2019-03-04+TO+2019-03-08]" -H "Authorization: Bearer [ACCESS_TOKEN]"
Example with the searchAfter
:
https://api.appmixer.com/logs?flowId=9c4673d7-a550-45a2-91c1-ad057fac0385&size=30&sort=gridTimestamp:asc&sort=_id:asc&query=gridTimestamp:[* TO *]&searchAfter=2023-10-25T15:03:17.721Z&searchAfter=wC5cZ4sB5dpG2lX0gmxi
The second value in the searchAfter
is the _id of a log record.
GET
https://api.YOUR_TENANT.appmixer.cloud/log/:logIndex/:logId
DEPRECATED. You can get the log details with /logs API:
curl "https://api.appmixer.com/logs?query=_id:AWKbQ6Vr9I6rzDWu4NbG&sort=@timestamp:desc" -H "Authorization: Bearer [ACCESS_TOKEN]"
Get a detail of a log. Log detail gives you information on the actual data of the message between two components.
curl "https://api.appmixer.com/log/93198d48-e680-49bb-855c-58c2c11d1857/appmixer-201804/AWKbQ6Vr9I6rzDWu4NbG" -H "Authorization: Bearer [ACCESS_TOKEN]"
POST
https://api.YOUR_TENANT.appmixer.cloud/logs
This method works the same as its /GET counterpart, but it also allows to get aggregations within the matched data, by passing a request body specifying desired aggregations.
GET
https://api.YOUR_TENANT.appmixer.cloud/telemetry
Get usage information.
curl "https://api.appmixer.com/telemetry?from=2018-03-17&to=2018-04-17" -H "Authorization: Bearer [ACCESS_TOKEN]"
GET
https://api.appmixer.com/telemetry/messages
Get usage information for a user identified by the userId
query parameter. This call requires admin privileges.
curl "https://api.appmixer.com/telemetry/messages?userId=54324413432141432&from=2024-01&to=2024-02 -H "Authorization: Bearer [ACCESS_TOKEN]"
GET
https://api.appmixer.com/telemetry/flows/:flowId
Get usage information for a flow identified by flowId
. This call requires admin privileges.
curl "https://api.appmixer.com/telemetry/flows/ef4324-431ff-434fadf-424 -H "Authorization: Bearer [ACCESS_TOKEN]"
You can natively embed Appmixer UI widgets into your application using the Appmixer UI JavaScript SDK. With just a few lines of code, you gain the ability to integrate not only the integration marketplace but also the fully-featured drag-and-drop automation designer. All widgets are customizable with your theme to align with your app's branding.
To incorporate the Appmixer SDK into your web page, follow these steps:
Note that YOUR_TENANT
is the name of your tenant. For example, if the URL of your hosted version of Appmixer is , then YOUR_TENANT
is eminent-emu-12345
.
Please refer to our page for additional methods to integrate the Appmixer SDK into your page
Initialize the Appmixer SDK by using the Appmixer API URL provided during the sign-up process. For hosted tenants, the URL follows the pattern: https://api.YOUR_TENANT.appmixer.cloud
.
Before presenting integrations to your end-users, it's necessary to first identify them. Achieve this by dynamically creating "Appmixer virtual users" in the background. These virtual users should be linked with the users in your own user management system.
The username
and usertoken
serve as credentials to authenticate Appmixer virtual users. It is advisable to utilize an email address (which can be fictional) as the username
and a secret token as the usertoken
. For instance, you might use MY_USER_ID_IN_MY_APP@yourappdomain.com
as the email format. Appmixer does not send emails autonomously to virtual users.
To create a usertoken
, consider using the function provided below:
Please ensure that you securely store these credentials alongside your user records associated with the Appmixer virtual users. This practice enables seamless authentication of your users with Appmixer upon their subsequent visits to your application.
Finally, you can embed your integration marketplace to your application. Utilize the ui.Integrations
and ui.Wizard
widgets offered by the Appmixer SDK to display the list of available and user activated integrations together with the Wizard web form, facilitating end-users in setting up new integrations or modifying existing ones:
Embedding the Integration Marketplace into your application is just the beginning. You can also incorporate the fully-featured drag-and-drop Automation Designer, granting your end-users unparalleled flexibility in integration and automation. Typically, when integrating the ui.Designer
, it's beneficial to include the ui.FlowManager
as well. This UI widget presents a list of automations previously created by your users, complete with capabilities for filtering, searching, and managing these automations.
The text within any UI widget embedded via the Appmixer SDK can be customized or translated into different languages. This is achieved by utilizing the strings
object, which can be directly passed to the Appmixer SDK constructor or configured using the set('strings', CHANGES)
method:
The Appmixer SDK offers a straightforward method to customize colors, fonts, and other visual attributes of the rendered UIs. To apply your custom theme, pass your theme object to the Appmixer SDK constructor, or configure it by using the set('theme', YOUR_THEME)
method:
For additional code examples on integrating Appmixer into your application, please visit our public repositories:
Create user. By default, this endpoint is open (does not require authentication). This can be changed by setting the API_USER_CREATE_SCOPE . If you set the value of API_USER_CREATE_SCOPE to for example admin
, then an admin token will be required to call this API.
curl -XPOST "https://api.appmixer.com/user" -H "Content-type: application/json" -d '{ "username": "abc@example.com", "email": "abc@example.com", "password": "abc321" }'
See the configuration for more details.
Create user. By default, this endpoint is open (does not require authentication). This can be changed by setting the API_USER_CREATE_SCOPE . If you set the value of API_USER_CREATE_SCOPE to for example admin
, then an admin token will be required to call this API.
curl -XPOST "https://api.appmixer.com/user" -H "Content-type: application/json" -d '{ "username": "abc@example.com", "email": "abc@example.com", "password": "abc321" }'
Although creating virtual users directly from client-side code is feasible, we recommend enhancing the security of your Appmixer tenant by configuring the API_USER_CREATE_SCOPE
system setting to the "admin
" string. This ensures that only admin users have the capability to add new users. To create virtual users, you should use the POST /user
API endpoint, accompanied by an access token from an Appmixer admin user, from your backend application code. For further details, please consult our documentation at .
For more information on customizing text strings and a comprehensive JSON object listing all the text placeholders available for customization, please refer to the page in our documentation.
For more detailed information, please refer to the page in the documentation.
password*
string
Password.
username*
string
Username, has to have an email format.
password*
string
Password.
string
Email address.
username
string
Email address.
scope
Array
Array of scopes.
vendor
String|Array
One or more vendors.
oldPassword*
String
Old password
newPassword*
String
New password
email*
String
User email address
password*
String
New password
email*
String
Email address
password*
String
New password. The minimum length of the password is five characters.
code*
String
Code generated via forgot-password.
password*
string
Password.
username
string
Username. If the email is not provided in the body, a username is required. Hence, a username or email must be included.
string
Email. If the username is not provided in the body, an email is required. Hence, a username or email must be included.
password*
string
Password.
email*
string
Email address.
username*
string
Username.
app
string
ID of an app as defined in service.json
or module.json
.
manifest
string
If set to "yes", the endpoint returns all components including their manifest files.
ticket
string
Ticket that you got from the POST /component request.
selector
string
A selector that uniquely identifies the service/module/component to delete. The selector is of the form [vendor].[service].[module].[component]. For example "appmixer.google.calendar.CreateEvent" (removes just one component) or "appmixer.google.calendar" (removes the calendar module) or "appmixer.google" (removes the entire Google service including all modules and components).
portType
string
string: in
or out
. Or it can be array portType=in&portType=out
. Used to filter only input messages or output messages. in and out
by default.
flowId
string
The flow ID to filter on. This parameter can be used multiple times to filter on more flows. If not present, it will return logs for all user's flows (even flows that are being shared with signed in user).
exclude
string
A comma separated field names to exclude from the log objects returned.
query
string
Query string. Uses the Lucene query syntax: https://lucene.apache.org/core/2_9_4/queryparsersyntax.html
sort
string
A parameter to sort the result. Optionally followed by ":desc" to change the order. asc
by default. This parameter can be passed multiple times to use multiple sortings.
size
number
Maximum number of logs returned. Useful for pagination. 50 records by default.
from
number
Index of the first log returned. Useful for pagination.
searchAfter
string
If using a sort parameter, this parameter can be used to specify from what value the logs will be fetch. Very useful for pagination and much more efficient than using from
parameter for this purpose. Can be passed multiple times if you are using a secondary sorting. This is usually to ensure that no duplicates are returned.
logId
string
Log ID. Use the "_id" property of the log object returned from flow logs.
logIndex
string
Log index. Use the "_index" property of the log object returned from flow logs.
flowId
string
The flow ID to filter on. This parameter can be used multiple times to filter on more flows.
exclude
string
A comma separated field names to exclude from the log objects returned.
query
string
Query string. Uses the Lucene query syntax: https://lucene.apache.org/core/2_9_4/queryparsersyntax.html
sort
string
A parameter to sort the result. Optionally followed by ":desc" to change the order. asc
by default. This parameter can be passed multiple times to use multiple sortings.
size
number
Maximum number of logs returned. Useful for pagination.
from
number
Index of the first log returned. Useful for pagination.
search_after
string
If using a sort parameter, this parameter can be used to specify from what value the logs will be fetch. Very useful for pagination and much more efficient than using from
parameter for this purpose. Can be passed multiple times if you are using a secondary sorting. This is usually to ensure that no duplicates are returned.
aggs
object
An object describing the desired aggregations. Uses Elasticsearch aggregation search structure: https://elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html
to
string
To date.
from
string
From date.
to
string
To date.
from
string
From date.
userId
string
A user ID.
ACLs can be used to control access to connectors by users or group of users or access to any other Appmixer functionality via limiting the API routes the users can use (API/UI).
GET
https://api.YOUR_TENANT.appmixer.cloud/acl-types
There are two types of access control lists, for components and for API routes. Restricted to admin users only.
GET
https://api.YOUR_TENANT.appmixer.cloud/acl/:type
Get list of all the ACL rules for given type. Restricted to admin users only.
type
string
components | routes
POST
https://api.YOUR_TENANT.appmixer.cloud/:type
Update ACL rule set for given type. Restricted to admin users only.
type
string
components | routes
array
Body has to be an array of ACL rules, where each rule has the following structure: { role: string - admin | user | tester ... resource: string - flows | appmixer.utils.* ... action: array of strings - * | read ... attributes: array of strings - * | non-private | ... }
GET
https://api.YOUR_TENANT.appmixer.cloud/acl/:type/resources
Get available values for resource property for an ACL rule. This is used for building UI in Backoffice for setting ACL rules. Restricted to admin users only.
type
string
components | routes
GET
https://api.YOUR_TENANT.appmixer.cloud/acl/:type/actions
Get available values for action property for an ACL rule. This is used for building UI in Backoffice for setting ACL rules. Restricted to admin users only.
type
string
components | routes
GET
https://api.YOUR_TENANT.appmixer.cloud/acl/:type/resource/:resource/attributes
Get available values for attributes property for an ACL rules. This is used for building UI in Backoffice for setting ACL rules. Restricted to admin users only.
type
string
components | routes
resource
string
resource name - flows, appmixer.utils.controls.*, ...
Appmixer allows you to upload files to use them in your flows.
GET
https://api.YOUR_TENANT.appmixer.cloud/files/metadata/:fileId
Get the information for the specified file. Note that the file content is not included.
fileId
string
The UUID of the required file.
POST
https://api.YOUR_TENANT.appmixer.cloud/files
Upload file to Appmixer. Uploads by chunks are supported, and whether if sent file is treated as a chunk or a new file depends on the headers sent. Also, Content-type
must be set to multipart/form-data
.
Content-type
string
Must be set to multipart/form-data
uploader-file-id
string
If set, the current file will be appended as a chunk to the file specified by this. If not present, a new file will be created. The response includes the resulting file's ID, so it can be used in subsequent requests to add chunks to the generated file.
uploader-file-name
string
The name of the file. This will be ignored in case that uploader-file-id header
is present.
uploader-chunk-size
string
The size in bytes of the file/chunk being uploaded.
uploader-chunk-number
string
This header is uploader-chunk-number
. The ordinal number of this chunk - e.g. the first chunk is 1, the second is 2 and so on.
uploader-chunks-total
string
This header is uploader-chunks-total
. The total number of chunks that compose the file. If set to 1, it means that this is the complete file
file
string
The file/chunk to be uploaded
GET
https://api.YOUR_TENANT.appmixer.cloud/files
Example: https://api.appmixer.com/files?limit=10&filter=filename:~invoice&sort=filename:1
The example will return 10 files with 'invoice' in the filename.
limit
number
offset
number
sort
String
projection
String
includeComponentSourceFiles
Boolean
By default, the endpoint returns only files (that are used in flows). Custom component source codes are stored as files as well, if the user owns some, they can be returned as well.
filter
String
GET
https://api.YOUR_TENANT.appmixer.cloud/files/count
Used for paging.
includeComponentSourceFiles
Boolean
By default, the endpoint returns only files (that are used in flows). Custom component source codes are stored as files as well, if the user owns some, they can be returned as well.
filter
String
DELETE
https://api.YOUR_TENANT.appmixer.cloud/files/:fileId
DELETE
https://api.YOUR_TENANT.appmixer.cloud/files
Example: DELETE https://api.appmixer.com/files?filter=filename:~invoice will delete all user's files with 'invoice' in the name.
filter
String
Can be used to filter files.
Access Data Stores (built-in key-value store).
GET
https://api.YOUR_TENANT.appmixer.cloud/stores
Get all key-value stores.
curl "https://api.appmixer.com/stores" -H "Authorization: Bearer [ACCESS_TOKEN]"
GET
https://api.YOUR_TENANT.appmixer.cloud/stores/:id
Get name of a store.
curl "https://api.appmixer.com/stores/5c6fc9932ff3ff000747ead4" -H "Authorization: Bearer [ACCESS_TOKEN]"
id
string
Store ID.
GET
https://api.YOUR_TENANT.appmixer.cloud/store/count
Get number of records in a store.
curl "https://api.appmixer.com/store/count?storeId=5c6fc9932ff3ff000747ead4" -H "Authorization: Bearer [ACCESS_TOKEN]"
storeId
string
Store ID.
GET
https://api.YOUR_TENANT.appmixer.cloud/store
Get records. Supports search and pagination.
curl "https://api.appmixer.com/store?storeId=5b213e0ef90a6200113abfd4&offset=0&limit=30&sort=updatedAt:-1" -H "Authorization: Bearer [ACCESS_TOKEN]"
storeId
string
Store ID.
sort
string
Store record parameter to sort by. Followed by ":" and the sort order -1 (descending) or 1 (ascending).
offset
number
Index of the first record returned.
limit
number
Maximum number of records returned.
POST
https://api.YOUR_TENANT.appmixer.cloud/stores
Create a new key-value store. Returns the newly created Store ID.
curl -XPOST "https://api.appmixer.com/stores" -H "Authorization: Bearer [ACCESS_TOKEN]" -H "Content-Type: application/json" -d '{ "name": "My Store" }'
name
string
Name of the store.
DELETE
https://api.YOUR_TENANT.appmixer.cloud/stores/:id
Delete a store and all the records in the store.
curl -XDELETE "https://api.appmixer.com/stores/5c7f9bfe51dbaf0007f08db0" -H "Authorization: Bearer [ACCESS_TOKEN]"
id
string
Store ID.
PUT
https://api.YOUR_TENANT.appmixer.cloud/stores/:id
Rename an existing store.
curl -XPUT "https://api.appmixer.com/stores/5c7f9bfe51dbaf0007f08db0" -H "Authorization: Bearer [ACCESS_TOKEN]" -H "Content-Type: application/json" -d '{ "name": "My New Name" }'
id
string
Store ID.
name
string
New name of the store.
POST
https://api.YOUR_TENANT.appmixer.cloud/store/:id/:key
Create a new value in the store under a key.
curl -XPOST "https://api.appmixer.com/store/5c7f9bfe51dbaf0007f08db0/mykey" -H "Authorization: Bearer [ACCESS_TOKEN]" -H "Content-Type: text/plain" -d "my value"
key
string
Key under which the posted value will be stored.
id
string
Store ID.
string
Value to store under the key.
PATCH
https://api.YOUR_TENANT.appmixer.cloud/store/:id/:key
Use this endpoint to rename a key or update the value against the key. Updates are passed in the body payload.
curl --location --request PATCH 'https://api.appmixer.com/store/623632fb3eb18366c82aa9fd/existingKey'
--header 'Authorization: Bearer [ACCESS TOKEN]'
--header 'Content-Type: application/json'
--data-raw '{ "key": "newKey", "value": "newValue" }'
id*
String
Store ID
key*
String
Key under which the updates are required
key
String
New key
value
String
New Value
DELETE
https://api.YOUR_TENANT.appmixer.cloud/store
Delete one or multiple items from a store.
curl -XDELETE "https://api.appmixer.com/store" -H "Authorization: Bearer [ACCESS_TOKEN]" -H "Content-Type: application/json" -d '[{ key: "mykey", storeId: "5c7f9bfe51dbaf0007f08db0" }, { "key": "mykey2", "storeId": "5c7f9bfe51dbaf0007f08db0" }]'
items
array
Array of items to delete. Each item is an object of the form { key, storeId }.
GET
https://api.YOUR_TENANT.appmixer.cloud/store/download/:storeId
The endpoint downloads the entire store either as a CSV or JSON file.
format
String
Default value 'json'. The other option is 'csv'.
Authentication to apps.
GET
https://api.YOUR_TENANT.appmixer.cloud/auth/:componentType
Get the list of accounts the user has previously authenticated with for this component type.
curl "https://api.acme.com/auth/appmixer.slack.list.SendChannelMessage?componentId=e15ef119-8fcb-459b-aaae-2a3f9ee41f15" -H "Authorization: Bearer [ACCESS_TOKEN]"
componentType
string
Component Type.
componentId
string
Component ID.
GET
https://api.YOUR_TENANT.appmixer.cloud/accounts
Get the list of all accounts the user has authenticated with to any component.
curl "https://api.appmixer.com/accounts" -H "Authorization: Bearer [ACCESS_TOKEN]"
filter
string
You can filter accounts.
Example of filtering certain accounts:
PUT
https://api.YOUR_TENANT.appmixer.cloud/accounts/:accountId
Update account information. Currently, only the display name can be updated. The display name is visible in the Designer inspector when selecting available accounts for a component type and also on the Accounts page.
curl -XPUT "https://api.appmixer.com/accounts/5a6e21f3b266224186ac7d03" -H "Authorization: Bearer [ACCESS_TOKEN]" -H "Content-Type: application/json" -d '{ "displayName": "My Account Name" }'
accountId
string
The ID of the account to update.
string
Human-readable name of the account.
POST
https://api.YOUR_TENANT.appmixer.cloud/accounts
This can be used to create an account. Usually, an account is created when the user authenticates a component. There are scenarios where it is beneficial to create an account without user interaction (Integrations). There has to be an authentication module (auth.js) installed in Appmixer corresponding to the `service` ID. All the built-in authentication types are supported (Oauth1, Oauth2, API Key).
validateScope
string
If false
, then the scope of the token from the body won't be validated against components installed in Appmixer.
requestProfileInfo
string
If false
, then the auth module requestProfileInfo function won't be called.
displayName
string
Display name property of the account. This overrides the name of the account in the frontend.
name
string
Name of the account, the authentication will determine the name of the account using the accountNameFromProfileInfo
property.
service
string
ID (vendor:service) of the service - `appmixer:google` for example.
token
object
The structure of this object depends on the authentication type (Oauth1, Oauth2, API Key).
profileInfo
object
Can be provided directly. If not, requestProfileInfo
from the authentication module will be called.
Below is an example of a request to create s Slack (Oauth2) account.
The profileInfo
object is optional. If you provide it it will be used. If you do not provide it then the requestProfileInfo
from the authentication module will be used to get the profile info. Slack access tokens do not expire, therefore there is neither an expiration date nor a refresh token in the request.
Below is another example, this time for Google (Oauth2) account with access token expiration:
An example, this time an API Key account:
Another example with a PWD account type:
POST
https://api.YOUR_TENANT.appmixer.cloud/accounts/:accountId/test
Test account. Check if all the credentials (tokens) are still valid for the account.
curl -XPOST "https://api.appmixer.com/accounts/5a6e21f3b266224186ac7d03/test" -H "Authorization: Bearer [ACCESS_TOKEN]"
accountId
string
Account ID.
DELETE
https://api.YOUR_TENANT.appmixer.cloud/accounts/:accountId
Remove the account and stop all the flows that this account is used in.
curl -XDELETE "https://api.appmixer.com/accounts/5a6e21f3b266224186ac7d03" -H "Authorization: Bearer [ACCESS_TOKEN]"
accountId
string
Account ID.
GET
https://api.YOUR_TENANT.appmixer.cloud/accounts/:accountId/flows
List all the flows where the account is used.
curl "https://api.appmixer.com/accounts/5a6e21f3b266224186ac7d03/flows" -H "Authorization: Bearer [ACCESS_TOKEN]"
accountId
string
Account ID.
POST
https://api.YOUR_TENANT.appmixer.cloud/auth/ticket
Generate an authentication session ticket. This is the first call to be made before the user can authentication to a service. The flow is as follows:
1. Generate an authentication session ticket.
2. Get an authentication URL.
3. Start an authentication session.
4. Open the authentication URL in a browser to start the authentication flow.
5. Once the user completes the authentication flow, the browser redirects the user to a special Appmixer page which posts a message of the form "appmixer.auth.[success/failure].[ticket]" via the window.postMessage() call: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage.
Note that this is a low-level mechanism that you don't have to normally deal with. The Appmixer JS SDK handles all this for you.
curl "https://api.appmixer.com/auth/ticket" -H "Authorization: Bearer [ACCESS_TOKEN]"
GET
https://api.YOUR_TENANT.appmixer.cloud/auth/:componentType/auth-url/:ticket
Get an authentication URL.
curl "https://api.appmixer.com/auth/appmixer.slack.list.SendChannelMessage/auth-url/58593f07c3ee4f239dc69ff7:1d2a90df-b192-4a47-aaff-5a80bab66de5" -H "Authorization: Bearer [ACCESS_TOKEN]"
ticket
string
Authentication ticket.
componentType
string
Component type.
string
Component ID.
GET
https://api.YOUR_TENANT.appmixer.cloud/auth/status/:ticket
DELETE
https://api.YOUR_TENANT.appmixer.cloud/auth/component/:componentId
Clear authentication associated with the component. Note that this call does not remove the account, it only removes the association of an account with a component.
curl -XDELETE "https://api.appmixer.com/auth/component/e25dc901-f92a-46a2-8d29-2573d4ad65e5" -H "Authorization: Bearer [ACCESS_TOKEN]"
componentId
string
Component ID.
PUT
https://api.YOUR_TENANT.appmixer.cloud/auth/component/:componentId/:accountId
Assign an account to a component.
curl -XPUT "https://api.appmixer.com/auth/component/e25dc901-f92a-46a2-8d29-2573d4ad65e5/5a6e21f3b266224186ac7d03" -H "Authorization: Bearer [ACCESS_TOKEN]"
accountId
string
Account ID.
componentId
string
Component ID.
GET
https://api.YOUR_TENANT.appmixer.cloud/flows
Return all flows of a user.
curl "https://api.appmixer.com/flows" -H "Authorization: Bearer [ACCESS_TOKEN]"
filter
string
Filter flows by their property values. Example: "userId:123abc" returns only flows who's owner is the user with ID "123abc" (i.e. shared flows are excluded). Note that you can also search on nested fields. This is especially useful with the customFields
metadata object. For example: "filter=customFields.category:healthcare".
sharedWithPermissions
string
Filter flows by their sharing setting. Example: "read,start". All possible permission are currently "read", "start", "stop".
projection
string
Exclude flow object properties. Example: "-flow,-thumbnail".
sort
string
Sorting parameter. Can be any flow object property followed by semicolon and 1 (ascending), -1 (descending). Example: "mtime:-1".
pattern
string
A term to filter flows containing pattern in their name or flowId property.
offset
number
The index of the first item returned. Default is 0. Useful for paging.
limit
number
Maximum items returned. Default is 100. Useful for paging.
GET
https://api.YOUR_TENANT.appmixer.cloud/flows/:id
Return one flow.
curl "https://api.appmixer.com/flows/9089f275-f5a5-4796-ba23-365412c5666e" -H "Authorization: Bearer [ACCESS_TOKEN]"
id
string
GET
https://api.YOUR_TENANT.appmixer.cloud/flows/count
Return the number of all flows of a user.
curl "https://api.appmixer.com/flows/count" -H "Authorization: Bearer [ACCESS_TOKEN]"
POST
https://api.YOUR_TENANT.appmixer.cloud/flows
Create a new flow.
curl -XPOST "https://api.appmixer.com/flows" -H "Content-Type: application/json" -d '{ "flow": FLOW_DESCRIPTOR, "name": "My Flow #1", "customFields": { "category": "healthcare" } }'
name
string
Name of the flow.
customFields
object
An object with any custom properties. This is useful for storing any custom metadata and later using the metadata values to filter returned flows.
thumbnail
string
Flow thumbnail image.
flow
object
Flow descriptor.
PUT
https://api.YOUR_TENANT.appmixer.cloud/flows/:id
Update an existing flow.
curl -XPUT "https://api.appmixer.com/flows/9089f275-f5a5-4796-ba23-365412c5666e" -H "Content-Type: application/json" -d '{ "flow": FLOW_DESCRIPTOR, "name": "My Flow #2" }'
id
string
Flow ID.
forceUpdate
boolean
A running flow cannot be updated unless forceUpdate=true.
object
An object with flow
, name
, customFields
and thumbnail
parameters. flow
is the Flow descriptor.
DELETE
https://api.YOUR_TENANT.appmixer.cloud/flows/:id
Delete an existing flow.
curl -XDELETE "https://api.appmixer.com/flows/9089f275-f5a5-4796-ba23-365412c5666e" -H "Authorization: Bearer [ACCESS_TOKEN]"
id
string
Flow ID.
POST
https://api.YOUR_TENANT.appmixer.cloud/flows/:id/clone
Clone a flow
id*
String
Flow ID
prefix
String
Prefix for flow clone name. The original flow name will be used with this prefix as a name for the new flow.
projection
String
Properties to be filtered from the flow model.
Example: "-thumbnail,-sharedWith". With this projection string, the thumbnail and sharedWith property will not be cloned.
connectAccounts
Boolean
If user accounts (like Gmail account for SendEmail component) connected to the source flow components should also be connected to cloned flow components. Default is false. Accounts can be connected only if the owner of the cloned flow is the same as the owner of the original flow.
isTemplateInstance
Boolean
If source flow is an instance of template (related to Integrations). Default is false
setOriginFlowId
Boolean
Default false. If true, the originFlowId property of the clone will be set to the original flow.
setComponentIdMap
Boolean
Default true. Stores a map of the component IDs from the original flow to the component IDs in the clone. This is later used in Integrations when performing updates of the Integration Templates.
POST
https://api.YOUR_TENANT.appmixer.cloud/flows/:id/coordinator
Start or Stop an existing flow.
curl -XPOST "https://api.appmixer.com/flows/9089f275-f5a5-4796-ba23-365412c5666e" -H "Content-Type: application/json" -d '{ "command": "start" }'
id
string
Flow ID.
command
string
The command to send to the flow coordinator. It can be either "start"
or "stop"
.
GET
https://api.YOUR_TENANT.appmixer.cloud/flows/:flowId/components/:componentId
Query Parameters
enqueueOnly
Boolean
If "true" then the response to this request will be returned as soon as the requests is enqueued in the engine. It will not wait for the component to process the request and create the response. The response code is 202 in this case. The default value is "false".
POST
https://api.YOUR_TENANT.appmixer.cloud/flows/:flowId/components/:componentId
Query Parameters
enqueueOnly
Boolean
If "true" then the response to this request will be returned as soon as the requests is enqueued in the engine. It will not wait for the component to process the request and create the response. The response code is 202 in this case. The default value is "false".
PUT
https://api.YOUR_TENANT.appmixer.cloud/flows/:flowId/components/:componentId
Query Parameters
enqueueOnly
Boolean
If "true" then the response to this request will be returned as soon as the requests is enqueued in the engine. It will not wait for the component to process the request and create the response. The response code is 202 in this case. The default value is "false".
DELETE
https://api.appmixer.com/flows/:flowId/components/:componentId
Query Parameters
enqueueOnly
Boolean
If "true" then the response to this request will be returned as soon as the requests is enqueued in the engine. It will not wait for the component to process the request and create the response. The response code is 202 in this case. The default value is
"false".
Global configuration for your connectors. If a component contains either an auth section or authConfig section, values for the specified service will be injected into the `context.config` object.
Only users with admin scope can use these endpoints.
GET
https://api.YOUR_TENANT.appmixer.cloud/service-config
Get a list of stored configurations.
pattern
string
A term to filter configurations containing pattern on their service id
sort
string
Sorting parameter. Service id can be used to sort results alphabetically by their id. Example: serviceId:1
offset
number
Index of the first item returned. Default is 0. Useful for paging.
limit
number
Maximum items returned. Default is 100. Useful for paging.
GET
https://api.YOUR_TENANT.appmixer.cloud/service-config/:serviceId
Get the configuration stored for the given service.
string
The service id. Example: appmixer:google
POST
https://api.YOUR_TENANT.appmixer.cloud/service-config
Creates a new configuration for a service. The only required parameter on the payload is the serviceId. The rest of the payload can be any key/value pairs that will be the desired configuration for the service. For example:
{
"serviceId": "appmixer:google",
"clientID": "my-global-client-id",
"clientSecret": "my-global-client-secret"
}
whatever
string
Any value for the whatever-key
serviceId
string
The serviceId. It should be in the form vendor:service
. Example: appmixer:google
PUT
https://api.YOUR_TENANT.appmixer.cloud/service-config/:serviceId
Updates the stored configuration for the given service. The payload should contain the whole configuration, as the payload content will overwrite the configuration stored under the service.
serviceId
string
The service id. Example
whatever-key
string
Any value you need
DELETE
https://api.YOUR_TENANT.appmixer.cloud/service-config/:serviceId
Removes the configuration from the given service.
serviceId
string
The service id. Example: appmixer:google
The People Task Appmixer module provides an API that lets you create tasks that can be approved or rejected by other people. This module is used by the appmixer.ui.PeopleTask UI SDK module in combination with the appmixer.utils.tasks.RequestApproval
and appmixer.utils.tasks.RequestApprovalEmail
components. Please see the People Tasks tutorial for more details.
Each task carries an email address of the requester and approver of the task together with title, description and due date. Tasks can have registered webhooks that the Appmixer engine calls when the status of the task changes (pending -> approved and pending -> rejected). Components can register these webhooks and trigger their outputs based on the result of the task resolution.
GET
https://api.YOUR_TENANT.appmixer.cloud/people-task/tasks
Return all tasks of a user.
secret
string
Approver or requester secret. This is the secret that you get in the approverSecret
or requesterSecret
property when you create a new task. This secret allows you to list tasks of any user for which you have the secret (instead of just the user identified by the token in the Authorization header).
limit
number
Maximum items returned. Default is 100. Useful for paging.
offset
number
The index of the first item returned. Default is 0. Useful for paging.
projection
string
Exclude task object properties. Example: "-description".
sort
string
Sorting parameter. Can be any task object property followed by semicolon and 1 (ascending), -1 (descending). Example: "decisionBy:-1".
filter
string
Filter tasks by their property values. Example: "status:pending" returns only pending tasks.
pattern
string
Filter tasks by a search term (searches the tasks title).
role
string
Filter tasks by role ("approver" or "requester").
GET
https://api.YOUR_TENANT.appmixer.cloud/people-task/tasks-count
Get the number of all tasks of a user.
secret
string
Approver or requester secret. This is the secret that you get in the approverSecret or requesterSecret property when you create a new task. This secret allows you to list tasks of any user for which you have the secret (instead of just the user identified by the token in the Authorization header).
filter
string
Filter tasks by their property values. Example: "status:pending" returns only pending tasks.
pattern
string
Filter tasks by a search term (searches the tasks title).
role
string
Filter tasks by role ("approver" or "requester").
GET
https://api.YOUR_TENANT.appmixer.cloud/people-task/tasks/:id
Get a task detail.
id
string
ID of a task.
POST
https://api.YOUR_TENANT.appmixer.cloud/people-task/tasks
decisionBy
string
Date by which the task is due. ISO 8601 format.
status
string
Status of the task. One of "pending", "approved" or "rejected".
description
string
The description of the task.
title
string
The title of the task.
requester
string
Requester's email address.
approver
string
Approver's email address.
POST
https://api.YOUR_TENANT.appmixer.cloud/people-task/webhooks
Register a new webhook URL for a task. Appmixer will send a POST request to this URL whenever the status of the task changes. This is usually done right after creating a new task so that you can get notified as soon as the task gets approved or rejected.
url
string
URL to be triggered when the status of the task changes.
taskId
string
ID of a task.
GET
https://api.YOUR_TENANT.appmixer.cloud/people-task/webhooks/:id
Delete a previously registered webhook.
id
string
Webhook ID, i.e. the id
returned from the /people-task/webhooks when registering a new webhook.
PUT
https://api.YOUR_TENANT.appmixer.cloud/people-task/tasks/:id
Edit an existing task.
id
string
Id of a task.
decisionBy
string
Date by which the task is due. ISO 8601 format.
status
string
The status of the task. One of "pending", "approved" or "rejected".
description
string
The description of the task.
title
string
The title of the task.
requester
string
Requester's email address.
approver
string
Approver's email address.
PUT
https://api.YOUR_TENANT.appmixer.cloud/people-task/tasks/:id/approve
This endpoint approves a task triggering all the registered webhooks for this task announcing a new approved
status.
id
string
ID of a task.
secret
string
Approver secret. This is the secret that you get in the approverSecret
property when you create a new task. This secret allows you to approve a task of any user for which you have the secret.
PUT
https://api.YOUR_TENANT.appmixer.cloud/people-task/tasks/:id/reject
This endpoint rejects a task triggering all the registered webhooks for this task announcing a new rejected
status.
id
string
ID of a task.
secret
string
Approver secret. This is the secret that you get in the approverSecret
property when you create a new task. This secret allows you to reject a task of any user for which you have the secret.
Modifiers are data transformation functions that can be used to transform data variables inside your flows. You can customize the list of modifiers or even define your own functions.
GET
https://api.YOUR_TENANT.appmixer.cloud/modifiers
Get available modifiers. Appmixer is shipped by default with its own set of modifiers by default. Checkout this API to see what they are. You can then redefine the default set with the following API endpoint.
PUT
https://api.YOUR_TENANT.appmixer.cloud/modifiers
Change the modifiers and their categories as a whole. Restricted to admin users only. Before editing existing modifiers or adding new ones, checkout the GET /modifiers API to see the structure.
DELETE
https://api.YOUR_TENANT.appmixer.cloud/modifiers
Delete all modifiers. Restricted to admin users only.
POST
https://api.YOUR_TENANT.appmixer.cloud/modifiers/test
This endpoint can be used to test a helper function with the desired arguments, to check how it will behave under different conditions.
curl -XPOST "https://api.appmixer.com/modifiers/test" -H "Content-type: application/json" -H "Authorization: Bearer [ACCESS-TOKEN]" -d '{ "helperFn": "function(value) { return value && value.hasOwnProperty('\''length'\'') ? value.length : 0; }", "arguments": ["test"]}'
Public files are files that are available at the root location of your Appmixer Tenant API endpoint. For example, uploading verify.html will make the file available at API_URL/verify.html.
GET
https://api.YOUR_TENANT.appmixer.cloud/public-files
The list returned does not contain the contents of the files.
POST
https://api.YOUR_TENANT.appmixer.cloud/public-files
curl --location 'https://acme.com/public-files' --header 'Authorization: bearer [the-admin-token]' --form 'file=@"/tmp/icon.png"' --form 'filename="icon.png"'
DELETE
https://api.YOUR_TENANT.appmixer.cloud/public-files/:filename
POST
/variables/:flowId/fetch
Get variables. Variables are placeholders that can be used in component config or inputs. These placeholders are replaced either at runtime by data coming from components connected back in the chain (dynamic variables) or by real values (static variables).
Headers
Body
Response
curl --location 'https://api.YOUR_TENANT.appmixer.cloud/variables/bfc64735-7cf0-4061-844c-e15a7147cbc7/fetch'
--header 'Authorization: Bearer [ACCESS_TOKEN]'
--header 'Content-Type: application/json'
--data '{ "useCache": true, "flow": false, "components": { "IDs": [ "bd9891a7-3303-43d4-a223-714a8db11e05" ], "properties": true, "links": false } }'
When a message processing fails, even after a certain number of retries, Appmixer stops processing the message and archives it. You can fetch, delete, and even retry those messages programmatically.
GET
https://api.YOUR_TENANT.appmixer.cloud/unprocessed-messages
Get the list of the unprocessed messages for the current user.
GET
https://api.YOUR_TENANT.appmixer.cloud/unprocessed-messages/:messageId
Get a single message.
DELETE
https://api.YOUR_TENANT.appmixer.cloud/unprocessed-messages/:messageId
Delete a message.
POST
https://api.YOUR_TENANT.appmixer.cloud/unprocessed-messages/:messageId
Put the message back into Appmixer engine.
Control the charts in a user dashboard (Insights UI).
POST
https://api.YOUR_TENANT.appmixer.cloud/charts
This method is not meant to be implemented by applications embedding Appmixer SDK. Creating chart requires complex objects (options, query, traces), their structure goes beyond this documentation. appmixer.ui.InsightsChartEditor
SDK component should be used to build Charts.
PUT
https://api.YOUR_TENANT.appmixer.cloud/charts/:chartId
The same properties as in Create Chart API endpoint.
GET
https://api.YOUR_TENANT.appmixer.cloud/charts
Get a list of all charts the user has configured in their Insights Dashboard.
GET
https://api.YOUR_TENANT.appmixer.cloud/charts/:id
DELETE
https://api.YOUR_TENANT.appmixer.cloud/charts/:id
System configuration. The following endpoints are only accessible to users with `admin` scope.
GET
https://api.YOUR_TENANT.appmixer.cloud/config
Only returns the values that have been stored by the user through the API
POST
https://api.YOUR_TENANT.appmixer.cloud/config
DELETE
https://api.YOUR_TENANT.appmixer.cloud/config/:key
All integration templates and automations are internally represented as flows. A flow consists of an orchestrated pattern of business activity enabled by interconnecting components together that transform input data (coming from trigger-type of components), perform actions, store data and/or load data to external systems.
Appmixer provides an interpreter for running flows and UI to manage flows.
Flows are represented as JSON objects in the Appmixer engine. The JSON object is called "flow descriptor" in the Appmixer jargon and for the example image above, it may look like this:
The flow descriptor contains information about the components in the flow and their types, how they are interconnected (source
), their properties (config.properties
) and data transformation for all input ports (config.transform
).
filename*
String
The name for the file
file*
File
The file to be uploaded
filename*
String
The name of the file you want to remove
Content-Type
application/json
Authorization
Bearer <token>
useCache
boolean
Default true
.
flow
boolean
Default true
. If true
, the response object will contain flow variables, too.
components
object
components.IDs
array<string>
Array of component IDs
components.links
boolean
Default true
. If true
, the response object will contain input link variables for the given component IDs.
components.properties
boolean
Default true
. If true, the response object will contains properties variables for the given component IDs.
categories
object
The object containing available modifier categories. Each category is composed by the following properties: - label: label to be shown in the Modifier Editor. It can be overridden by string customization. - index: position on which this category is displayed in the Modifier Editor.
modifiers
object
The object containing available modifiers. Each modifier is composed of the following properties:
- label: label to be shown in the Modifier Editor. Can be overridden by string customization.
- category: an array containing the categories which the modifier will appear under.
- description: short description of the modifier. It will appear under modifier's label. Can be overridden by string customization.
- arguments (optional): an array of objects describing the arguments for the modifier if there is any. For each argument, an inspector field is generated in the editor. The structure of each object is {
name: String,
type: String,
isHash: Boolean, Optional
}
- returns (optional): an object with just one property type indicating the expected return type.
- isBlock (optional): boolean. Indicates if this is a block modifier.
- private (optional): boolean. Indicates if the modifier is private and therefore not available for users.
- variableArguments (optional): boolean. If set to true the modifier accepts any number of arguments. The inspector will render an "Add" button to add as many arguments as the user wants.
- helperFn: the javascript function as a string, which does the transformation of the variable. The function definition can have arguments being the first one always the variable value, and the subsequent each of the modifier's arguments, in the same order they are defined on arguments
array.
helperFn
string
The Javascript helper function as a string.
arguments
array
The arguments to be passed to the helper function.
string
messageId
string
messageId
string
messageId
string
traces
object
The aggregations that are represented on the chart along with their sources (flows, components).
query
object
Object representing time range for the chart.
options
object
Object with the visualization options for the chart.
index
number
The position of the chart in the dashboard.
type
string
Type of the chart. bar, line, scatter, area, pie
name
string
Name of the chart.
string
pattern
string
Regex that will be used to match name
property.
limit
number
Maximum items returned. Default is 100. Used for paging.
offset
number
The index of the first item returned. Default is 0. Used for for paging.
sort
string
Sorting parameter. Can be any chart object property followed by semicolon and 1 (ascending) or -1 (descending). Example: "mtime:-1".
projection
string
Exclude chart object properties. Example: "-traces".
id
string
ID of the chart to return.
id
string
ID of a chart.
key*
String
Configuration key
value*
Any
Configuration value
key*
String
The key of the configuration to be removed
(optional)
The label of your component. If not label is specified, then last part of name
will be used when component is dropped into Designer. If your component name is appmixer.twitter.statuses.CreateTweet
then CreateTweet
will be name of the component unless you specify label
property. This allows you to use spaces as opposed to the name
property.
The icon representing the component in the UI. It must be in the Data URI image format as described here: https://en.wikipedia.org/wiki/Data_URI_scheme. image/png
or image/svg+xml
image types are recommended. Example:
(required)
The name of your component. The name must have the following format: [vendor].[service].[module].[component]. Note that all the parts of the name must contain alphanumeric characters only. For example:
The vendor
part of the component name is the ID of the author of the component set. service
and module
allows you to organize your components into categories. These categories not only help you keep your components in a tidy hierarchical structure but it also has a meaning in that you can share your authentication and quota definitions between modules and components (more on that later). component
describes the actual component activity.
The component manifest provides information about a component (such as name, icon, author, description and input/outputs definition) in a JSON text file. The manifest file must be named component.json.
Example manifest file:
Configuration of the quota manager used for this component. Quotas allow you to throttle the firing of your component. This is especially useful and many times even necessary to make sure you don't go over limits of the usage of the API that you call in your components. Quota managers are defined in the quota.js
file of your service/module. Example:
The name of the quota module where usage limit rules are defined.
One or more resources that identify rules from the quota module that apply to this component. Each rule in the quota module can have the resource
property. quota.resources
allow you to cherry-pick rules from the list of rules in the quota module that apply to this component. quota.resources
can either be a string or an array of strings.
This scope instructs the quota manager to count calls either for the whole application (service) or per-user. Currently, it can either be omitted in which case the quota limits for this component apply for the whole application or it can be { "userId": "{{userId}}" }
in which case the quota limits are counted per Appmixer user.
When set to true
, the component will not be visible to end users.
By default, you can send GET, POST, PUT, or DELETE HTTP requests to a component. With the property httpRequestMethods
you can define, which HTTP methods are allowed. Additionally, you can add support for the OPTIONS method:
The author of the component. Example:
The definition of the input ports of the component. It's an array of objects.
Each component can have zero or more input ports. If a component does not have any input ports, we call it a trigger. Input ports allow a component to be connected to other components. Input ports receive data from output ports of other connected components when the flow is running and the data is available. Each input port has a name
and configuration that has the exact same structure as the configuration of properties
, i.e. it has schema
, inspector
or source
objects. The difference is that the user can use placeholders (variables) in the data fields that will be eventually replaced once the actual data is available. The placeholders (variables) can be entered by the user using the "variables picker" in the Designer UI inspector (see below). Example:
The message
from the example looks like this in the raw form:
As you can see, the placeholders for variables use a special format that the Appmixer engine eventually replaces with real values that come from the GetCurrentWeather component once the data is available.
Definition of the schema of the data that the input port expects. Please see the Properties Schema section for more details.
Definition of the inspector UI for this input port. Please see the Properties Inspector section for more details.
Definition of the source of the variables or dynamic inspector that will be available in the designer UI for this input port.
An example of how the source property can be used to generate the input port Inspector dynamically for the appmixer.google.spreadsheets.CreateRow component. When showing the Inspector for the CreateRow, we need to know the structure (columns) of the Worksheet, the Inspector input fields will copy the columns in the Worksheet
Note how we mapped the configuration properties of the CreateRow
component to the configuration properties of the ListColumns
component that is called internally to retrieve the list of columns. The data.properties
section defined property mappings. To map input port values, you can use the data.messages
section instead. Let's see another example of a component, this time with input message mappings, the trello.list.CreateCard
component inspector definition:
As you can see, the boardId
(referenced to by the "inputs/in/boardId"
syntax) configured by the end-user on the CreateCard
component is mapped into the input port property of the same name of the ListBoards
component ("in/boardId"
).
This object allows you to control what variables will be available to this component in the UI and in the component receive()
method. By default, variables are collected from all the components back in the chain of connected components. This might not be desirable in some cases. One can set scopeDepth
to a number that represents the depth (levels back the graph of connected components) used to collect variables. rawValue
can be used to tell the engine not to resolve variable placeholders to their actual values but to treat variable names as values themselves. Example:
Set the maximum number of links that can be connected to the input port. Maximum number of connections is infinite by default but in some applications, it might be desirable to set a limit on this, usually 1
. The Appmixer Designer UI will not allow the user to connect more than maxConnections
links to the input port.
Components are the building blocks of integrations and automations in Appmixer. Each component in a flow reacts on incoming messages, processes them and produces outgoing messages. User can wire components together to define complex workflows and integrations. Usually, components call external APIs but they can also do some internal processing, logic or scheduling.
Take the example above. There are three components in the flow. The first one (Timer) we call a trigger because it does not have any input ports and so the component generates outgoing messages based on its internal logic. In our case, the Timer component sends messages to its output port out in regular intervals that the user can specify in the UI. As soon as a message leaves an output port, it travels through all the connected links to input ports of other connected components. In our scenario, when a message leaves the out port of our Timer, it goes to the location input port of the GetCurrentWeather component. As soon as the GetCurrentWeather component receives a message on its input port, it starts processing it. In this case, it requests current weather information from the https://openweathermap.org API. Once a response is received from the API, the component continues to send the result to its output port weather. Note that the location for which we're requesting the current weather can be specified by the user in the UI. The process then repeats for all the other connected components until no message is generated on an output port or there is no other component connected.
To make our example flow complete, it is important to note that any component can be configured using data generated on output ports of any component back in the chain of connected components. In our example, our SendChannelMessage component sends a message on Slack channel #qa-messages with text containing the city, humidity, pressure and temperature as it was received from the weather API. The user configures the flow in the designer UI simply by selecting placeholders (variables in the Appmixer jargon) that will eventually be replaced when the flow goes to the running state and the actual data is available.
Components have some important properties that we should mention before diving into the details:
Components don't know about each other. All components are totally independent and loosely coupled. They only react on incoming messages and produce outgoing messages. The linkage between components is not internal to the components themselves but rather a mechanism of the Appmixer internal engine and its protocol.
Components are black-boxes to the Appmixer engine. The engine does not know and also does not need to know what components internally do and how they are implemented. It only wires them together through ports and makes sure messages are always delivered and in the right order.
Connectors in Appmixer are structured into "services", "modules" and "components" hierarchy. Each service can have multiple modules and each module can have multiple components. For example, "Google" service can have "gmail", "calendar" or "spreadsheets" modules and "gmail" module can have "SendEmail", "NewEmail" and other components:
This hierarchy is reflected in the directory structure of component definitions. Typically, services and modules are structured in two ways. Either the service itself appears as an "app" in Appmixer or modules are separate apps. If a module has its own manifest file (module.json
), it is considered a separate app in Appmixer.
For example, in case of Google, we want to have separate apps for each module (GMail, Calendar, Analytics, ...):
But in case of Twilio, we may just want to have one app and all the actions/triggers as different components of the Twilio app:
As mentioned in the previous section, services, modules and components must follow the service/module/component directory structure. The following images show the two different ways you can structure your services (i.e. modules as separate apps or a service as one single app).
Service manifest is defined in the service.json
file. The file has the following structure:
Available fields are:
Field
Description
name
label
The label of your app.
category
App category. By default, components shipped with Appmixer are divided into two categories "applications" and "utilities" but you can have your own custom categories too. Just use any custom category name in the service manifest file to create a new category and add your service to it. This category will become automatically visible e.g. in the Appmixer Designer UI.
categoryIndex
App category index. By default, categories are sorted alphabetically, you can change that using this index property. Optional.
index
The app index within the category. This allows sorting the apps within the same category.
description
Description of your app.
icon
App icon in the Data URI format.
Module manifest is defined in the module.json
file. The file has the following structure (similar to the service.json
file):
Available fields are:
Field
Description
name
label
The label of your app.
category
App category. By default, components shipped with Appmixer are divided into two categories "applications" and "utilities" but you can have your own custom categories too. Just use any custom category name in the module manifest file to create a new category and add your app to it. This category will become automatically visible e.g. in the Appmixer Designer UI.
categoryIndex
App category index. By default, categories are sorted alphabetically, you can change that using this index property. Optional.
index
The app index within the category. This allows sorting the apps within the same category.
description
Description of your app.
icon
App icon in the Data URI format.
The authentication service and parameters. For example:
The auth.service
identifies the authentication module that will be used to authenticate the user to the service that the component uses. It must have the following format: [vendor]:[service]. The Appmixer engine looks up the auth.js
file under that vendor and service category. auth.scope
provides additional parameters to the authentication module. See the Authentication section for more details.
When auth
is defined, the component will have a section in the Designer UI inspector requiring the user to select from existing accounts or connect a new account. Only after an account is selected the user can continue configuring other properties of the component.
Description of your component. The description is displayed in the Designer UI inspector panel like this:
The description should not be longer than a sentence or two. Example:
The definition of the output ports of the component. It's an array of objects.
Components can have zero or more output ports. Each output port has a name
and optionally an array options
that defines the structure of the message that this output port emits. Without the options object, the user won't be able to see the possible variables they can use in the other connected components. For example, a component connected to the weather
output port of our GetCurrentWeather component can see the following variables in the variables picker:
An example of an outPorts
definition can look like this:
We support full schema definition for each option, so you can specify the structure of the data that is coming out from your component. You can add a schema
property to each option, which contains a JSON Schema definition. For example:
As you can see, compared to the first example, we replaced the last 3 options with a single one, which is actually an array of items with three properties. Each of these items has title
which determines the label that will be visible in the UI. Note that the type of these inner properties could be an object
or array
, and have their own nested schemas.
If the option
is defined as an array
and you want to work with that array using modifiers:
You will see the item properties
among other variables.
And if you use the Each connector, you will see the item properties
in the Variables picker.
Alternatively, you can define a schema at the top level instead of using the options property. For example:
When you define the structure of the data coming out from your component, the users of your component will have an easier time working with it, as they will be able to do things like selecting nested properties directly, selecting properties on iteration modifiers, and getting properties paths in modifiers. You can find more details about this in this section.
The definition is similar to the source source
of properties. When used for the output port definition, it allows defining the output port schema dynamically.
There is one difference though. When defined in the output port, the source
definition can reference both component properties and input fields, while the properties source
definition can only hold references to other properties' values.
An example is a Google Spreadsheet component UpdatedRow. The output port options of this component consist of the column names in the spreadsheet. But that is specific to the selected Spreadsheet/Worksheet combination. Therefore it has to be defined dynamically.
Here is an example of the UpdatedRow output port definition.
Set the maximum number of outgoing links that can exist from the output port. The maximum number of connections is infinite by default but in some applications, it might be desirable to set a limit on this, usually 1
. The Appmixer Designer UI will not allow the user to connect more than maxConnections
links from the output port.
The configuration properties of the component. Note that unlike properties specified on input ports (described later on in the documentation), these properties cannot be configured by the user to use data coming from the components back in the chain of connected components. In other words, these properties can only use data that is known before the flow runs. This makes them suitable mainly for trigger type of components.
Configuration properties are defined using two objects schema
and inspector
.
schema
is a JSON Schema definition (http://json-schema.org) of the properties, their types and whether they are required or not. An example looks like this:
The JSON Schema gives you enough flexibility to describe your property types and the required format, possibly using regular expressions or other mechanisms. When the user fills in the forms in the Designer UI inspector to configure their components, the Designer automatically validates all inputs using the schema. If any of the properties are invalid, the Designer UI gives an immediate feedback to the user that they should correct their configuration:
inspector
tells the Designer UI how the input fields should be rendered. The format of this definition uses the Rappid Inspector definition format. Example:
Do not use special characters .
or /
in the name of the input.
As you can see, fields (e.g. interval
in this case) are nested inside the inputs
object and have the following properties:
type
can be any of the built-in types. See below for more details. (Custom inspector fields are also possible for on-prem installations. See the Custom Inspector Fields page for more details.)
group
is an identifier of an Inspector group this field belongs to. As you can see in the example above, you can have one or more custom groups (like config
in this case) that you can define in the groups
object. Groups will render in the Inspector UI in an accordion-like fashion. This is handy to organize your fields.
label
is a short text that appears above your input field. This is a great place to tell your users what your field is.
A single line input field.
A multi-line text input field.
A numerical input field. Additional configuration includes min, max and step numbers.
A menu of options. Options are defined in the options
array each item having content
and value
properties. Note that content
can be HTML. You can optionally provide placeholder
that is displayed if no option is selected. Default values can be defined with defaultValue
. If you need one of the items to clear the value of the select input field, use { "clearItem": true, "content": "Clear" }
as one of the objects in the options
array.
Similar to select
type, multiselect defines options the user can choose from. The difference is that with multiselect, the user can select multiple options, not only one. The value stored in the flow descriptor is an array of values the user selected. Supported options are options
and placeholder.
A date-time input field allows the user to select a date/time using a special date/time picker interface. The date-time input field can be configured to support different type of formats or modes (only date or date-time combination). The configuration is stored in the "config" object. The following table shows list of all the available options:
Option
Description
format
enableTime
Boolean. Enables time picker.
enableSeconds
Boolean. Enables seconds in the time picker.
maxDate
String representing the maximum date that a user can pick to (inclusive).
minDate
String representing the minimum date that a user can pick to (inclusive).
mode
Mode of the date/time picker. Possible values are "single"
, "multiple"
, or "range"
.
time_24hr
Boolean. Displays time picker in 24 hour mode without AM/PM selection when enabled.
weekNumbers
Boolean. Enables display of week numbers in calendar.
A toggle input field allows the user to switch between true/false values.
A menu of colors. Colors are defined in the options
array each item having content
and value
properties, where values
must be a color in any of the CSS color formats (named-color, hex-color, rgb() or hsl()).
A group of toggle buttons. Both single and multiple selection is allowed (can be controlled with the multi
flag). Buttons are defined in the options
array each item having value
, content
and icon
properties.
A multi-field type field that allows for the definition of logical expressions (OR/AND) or dynamic field definitions. This field accepts a list of other inspector field types (text, textarea, number, toggle, ....) and renders a "field builder" UI that enables the user to dynamically create nested fields.
The value of this field has the following structure:
Note that by specifying the levels
option, you can define the nesting. Currently, a maximum of 2 levels of nesting is supported. The common use case is to use just one level. In that case, set e.g. "levels": ["ADD"]
.
The exclusiveFields
is an optional property that defines the fields that will use variables in an exclusive way. For example, let's say that the component has variableA
and variableB
available for use in its fields. Now if the myText
field is in exclusiveFields
array which means that you can use each variable once across all the fields inside the expression groups. To clarify this further, imagine the following scenario configuring an expression type:
Click the ADD
button to create a second group.
Select variableA
on the myText
field inside the first group using the variables picker.
When opening the variables picker in myText
field inside the second group, only variableB
will be available, because variableA
is already been used.
The expand
option in source
. The select
inputs in the expression
can have dynamic values retrieved with the source
configuration (just like an ordinary select
input). Sometimes you may want to define different dynamic values for every expression box based on another field(s) in that box:
This is how it is done:
An input that allows selecting and uploading files from the user's computer. When clicked, it will open the browser's file selector, and the file selected will be uploaded to Appmixer and referenced on the input.
Similar to the filepicker input, this one allows users to select files or folders on their Google Drive accounts. When clicked a Google Drive file picker is opened, showing the user's Google Drive content. When selecting a folder/file, the input value becomes an object which includes the Id of the folder/file which should be used on Google API calls to reference that asset.
You can use googlepicker to pick folders instead of files:
This input type needsappmixer.google.drive.GooglePicker
component to be installed.
Similar to the googlepicker, this one allows users to select files or folders from their OneDrive accounts. When clicked, an OneDrive file picker is opened, showing the user's OneDrive content. When selecting a folder/file, the input value becomes an object which includes the id of the folder/file which should be used on OneDrive API calls to reference that asset.
The view property works similar to the same property on googlepicker. It can be used to determine what is shown on the picker. You can use 3 values: files
, folder
, all
. As their names indicate, if select files
, only files will be shown, if you select folder
it will show only your folders and if you select all
it will show both. This input type needs appmixer.microsoft.onedrive.OneDrivePicker
component to be installed.
There are some cases when you want to show input fields depending on other values in the inspector. This allows to a better UX for component configuration. For this we use the when
property in the field we want to be conditional:
The when field has the following structure: { op: { field: comparisonValue }}
.
op: Is the operand that will be used to determine if the condition holds true or false. The following operands are supported:
eq
: Equality between the values.
equal
: Equality between the values by deep comparison. Used for objects and arrays.
ne
: Values are not equal.
regex
: Check if the value in given field
matches the regex in the comparisonValue
.
text
: Check if the value in the given field
contains the string in the comparisonValue
.
lt
: Check if the value in the given field
is less than the comparisonValue
.
lte
: Check if the value in the given field
is less or equal than the comparisonValue
.
gt
: Check if the value in the given field
is greater than the comparisonValue
.
gte
: Check if the value in the given field
is greater or equal than the comparisonValue
.
in
: Check if the value in the given field
is included on the given comparisonValue
array.
nin
: Check if the value in the given path is not included in the given comparisonValue
.
field: The field that is used for comparison, there are several ways to reference the field:
field
: The same form presented in the example. It will search the given fields in current input port fields.
properties/someProperty
: Refer to a property inside component properties.
./field
: It will refer to sibling fields of the current field. Specially useful when working with expression types.
comparisonValue: The value used to compare the field against.
As it was mentioned, conditional fields also work with expression types, allowing to control the field rendering inside those expressions:
Sometimes the structure of the inspector is not known in advance and it cannot be hardcoded in the manifest. Instead, the inspector fields are composed dynamically based on the data received from an API. A good example is the google.spreadsheets.CreateRow component where the inspector renders fields representing columns fetched from the actual worksheet. For this to work, we can define the source
property in the manifest that calls a component of our choosing in a so called "static" mode. For example:
In the example above, we call the ListColumns component and we're interested in the output coming from the output port out
.Since this is just a normal component, we need to transform the result into the inspector-like object, i.e.:
We need to tell Appmixer where it can find the transformation function. For this we use the transform
property which tells Appmixer to look for the transformers.js
file inside the ListColumns/
directory. The transformer must return an object with a function named columnsToInspector
that can look like this:
A special URL that identifies a component that should be called in a "static" mode. It has to be of the form /component/[vendor]/[service]/[module]/[component]
. It should also contain outPort
in the query string that point to the output port in which we're interested to receive data from. Example:
Messages that will be sent to the input port of the component referenced by the properties.source.url
. Keys in the object represent input port names and values are any objects that will be passed to the input port as messages.
Properties that will be used in the target component referenced by the properties.source.url
. The target component must have these properties defined in its manifest file. The values in the object are references to the properties of the component that calls the target component in the static mode. For example:
The transformation function used to transform the output of the target component. It should return an inspector-like object, i.e.:
Example:
The transform function is pointed to be a special format [module_path]#[function]
, where the transformation module path is relative to the target component directory.
Set webhook
property to true
if you want your component to be a "webhook" type. That means that context.getWebhookUrl()
method becomes available to you inside your component virtual methods (such as receive()
). You can use this URL to send HTTP requests to. See the section, especially the for details and example.
When set to true
, the component will receive signals in regular intervals from the engine. The tick()
Component Virtual method will be called in those intervals (see ). This is especially useful for trigger-type of components that need to poll a certain API for changes. The polling interval can be set by the COMPONENT_POLLING_INTERVAL
environment variable (for custom on-prem installations only). The default is 60000 (ms), i.e. 1 minute.
Set state
property to { persistent: true }
to tell the engine not to delete component state when flow is stopped. See for more information.
The name of the service. The name must have the [vendor].[service]
format where [vendor]
is the Vendor name (See e.g. for more details). Normally you'll have just one vendor or use the default 'appmixer'
vendor. [service]
is the name of your service. Example: "appmixer.google"
, "appmixer.twilio"
, ... .
The name of the module. The name must have the [vendor].[service].[module]
format where [vendor]
is the Vendor name (See e.g. for more details). Normally you'll have just one vendor or use the default 'appmixer'
vendor. [service]
is the name of your service and [module]
is the name of your module. Examples: "appmixer.google.gmail"
, "appmixer.google.calendar"
, .... . Note that the directory structure of your module must follow this name. In other words, if you have a module named "appmixer.myservice.mymodule"
, your directory structure will look like this: myservice/mymodule.
String representing the format of the date/time. Please see the moment.js library documentation for all the available tokens: .
An optional object containing localization strings. For example:
For more information about component localization, refer to the Custom Component Strings section.
Components can use 3rd party libraries which are defined in the standard package.json
file. An example:
The package.json
file from the example above tells Appmixer to load the twilio
library that the appmixer.twilio.sms.SendSMS
component requires for its operation.
Note that the appmixer pack
command from the Appmixer CLI ignores the node_modules
directory when creating the zip archive representing your custom component. This is intended since when you publish a component to your Appmixer tenant, Appmixer will automatically download dependencies specified in the package.json
file.
More information on the package.json
file can be found at https://docs.npmjs.com/files/package.json.
Manage records associated with data storage utility components of flows.
Set up a new instance with config
parameters and set
/get
methods:
config.el
...
Learn about widget
config
here.
config.storeId
Type: String
| Default: []
ID of a store to open within the storage.
Learn about widget
instance here.
loader
Type: Boolean
| Default: null
Toggle a custom loading state.
error
Type: String
| Default: null
Toggle a custom error message.
The marker icon that can be added to the component in the UI to give some extra context. The most common use case is to display e.g. a "Beta" badge to tell the user that this component is in beta. The marker must be in the Data URI image format as described here: https://en.wikipedia.org/wiki/Data_URI_scheme. image/png
or image/svg+xml
image types are recommended. The marker icon is displayed in the top right corner of the component shape.
Example:
Fire patterns is an advanced configuration of a component that allows you to define when your component is ready to fire (ready to process input messages). Fire patterns can make the engine to hold input messages on components input ports until the pattern matches and then send the messages to the component in bulk. Fire patterns are defined as an array or a matrix. An example of fire patterns may look like this:
The fire pattern above is interpreted as follows: The component processes messages only if the first input port has zero or more messages waiting in the queue and at least one message waiting in the second input port queue. Another example can be a fire pattern:
In this case, the component only processes messages if there is at least one message on each of its two input ports. A good example for this pattern is the Sum component:
The Sum component expects messages on both of its input ports before it can produce a sum of its inputs.
The following table lists all the possible fire pattern symbols:
Note that you can also define a set of fire patterns for a component, for example:
When more fire patterns are used, there must be at least one fire pattern that matches before the component fires.
Symbol
Description
*
(Any) The input port must have zero or more messages in the queue.
1
(Exists) The input port must have at least one message in the queue.
0
(Empty) The input port must have no message in the queue.
A
(All) The input port must have at least one message from all the connected components in the queue. This is a synchronization pattern that lets you specify that the component must wait for all the connected components to send a message before it can start processing. A typical example is a "Multiple-to-Single" join component. This component must wait for all the LoadCSV components to send a message before it can produce an SQL-like join schema.
Refer to the Embed into Your Application getting started guide to get started quickly.
Appmixer SDK package includes two types of modules: basic UMD and advanced ESM.
Load appmixer.js
UMD module in your HTML file:
See the following repositories for more comprehensive demos on how Appmixer can be embedded in your apps https://github.com/clientIO/appmixer-demo-embedded-integrations, https://github.com/clientIO/appmixer-demo-firebase-vanilla.
If you're using a Self-Managed Appmixer package, you should link your own Appmixer JavaScript SDK from your own Appmixer Studio URL (i.e. instead of https://my.YOUR_TENANT.appmixer.cloud, you will reference your own Studio URL). Alternatively, you can download the appmixer.js file and link to it from whatever location you will put it in.
Download appmixer.es.js
ES module and include the files in your project:
Choose Appmixer UI widgets to include:
Appmixer SKD is a toolkit to embed workflow automation and integration capabilities into your products. Gain a whole new set of comprehensive features with ease.
The majority of APIs define limits on the API usage. Components that call APIs need to make sure that these limits are respected, otherwise their API calls would start failing quickly. The quota.js
module allows you to specify what those limits are on a per-service, per-module or even per-component basis. The Appmixer engine uses this module to make sure API calls are throttled so that the usage limits are respected.
The quota module must be named quota.js
and must be stored under either the service, module or component directory (i.e. [vendor]/[service]/quota.js
, [vendor/[service]/[module]/quota.js
or [vendor/[service]/[module]/[component]/quota.js
.
An example of a quota module:
The quota definition above tells the engine to throttle the receive()
call of the component to a max of 2000-times per day and 3-times per second.
Quota modules are NodeJS modules that return an object with one property rules
.
An array of rules that define usage limits. Each rule can have the following properties:
Maximum number of calls in the time window specified by window
.
The time window in milliseconds.
The throttling mechanism. Can be either a string 'window-sliding'
or an object with type
and getStartOfNextWindow
function. Example of a quota module for LinkedIn:
An identifier of the resource to which the rule applies. The resource is a way for a component to pick rules that apply to that specific component. This can be done in the component manifest file in the quota.resources
section.
You can also configure system webhook to receive the quota errors when raised. Read more about it here.
Appmixer Constructor lays a foundation for building user interfaces with widgets.
Set up a new appmixer
instance with configuration parameters passed directly into the constructor or use the set
/get
methods:
baseUrl
Type: String
| Default: null
Base URL of your Appmixer engine REST API.
accessToken
Type: String
| Default: null
Access token of an authorized user.
debug
Type: Boolean
| Default: false
Enable debugger for development purposes.
theme
Type: Object
| Default: DefaultTheme
l10n
Type: Object
| Default: DefaultL10N
Define custom localization texts.
lang
Type: String
| Default: en
Specify a language code for the localization of components.
api
Type: Object
| Default: {}
Set custom API methods.
appmixer.ui
Register and create UI Widgets.
appmixer.api
Use methods of built-in API Module.
appmixer.set
Set configuration property.
appmixer.get
Get configuration property.
appmixer.registerCustomComponentShape
Register a custom Designer component shape.
appmixer.registerInspectorField
Register a custom Designer inspector field.
Connectors that require authentication from the user must implement the authentication module. The authentication module must be named auth.js
and must be stored under either the service or module directory (i.e. [vendor]/[service]/auth.js
or [vendor/[service]/[module]/auth.js
. Appmixer currently supports four types of authentication mechanisms out-of-the-box that are common for today's APIs: API key, Password, OAuth 1, and OAuth 2.
Appmixer provides an easy way to configure authentication modules. Most of the time, it's only about configuring a 3rd party service provider URLs for authentication, requesting access tokens, and token validation.
Each authentication module is a NodeJS module that returns an object with type
and definition
properties. type
can be either apiKey
, pwd
, oauth
(for OAuth 1) and oauth2
(for OAuth 2). definition
is either an object or a function (useful in cases where there's a code that you need to run dynamically).
The type of authentication mechanism. Any of apiKey
, pwd
, oauth
and oauth2
.
The definition of the authentication mechanism is specific to the API service provider. An object or a function. This differs significantly between authentication mechanisms.
If the definition property is specified as a function, it has one argument called context. It is an object that contains either consumerKey, username and password, consumerSecret (OAuth 1) or clientId, clientSecret (OAuth 2) and it always contains callbackUrl property. This will be shown later in the examples.
As was mentioned in the beginning, Appmixer authentication supports four mechanisms: API Key, Password, OAuth 1, and OAuth 2. Each of them has a different definition.
In the following examples, we will show how a particular property of the definition object can be specified as a function, object, or string. Let's demonstrate that on a requestProfileInfo property since it is common for all authentication mechanisms.
This is the most basic type of third-party authentication. In order to use this mechanism, type
property must be set to apiKey
. Here is an example from Freshdesk components:
Next, we explain the fields inside the definition object:
This is the definition for the Web Form that will be displayed to the user to collect information required by the third-party application. Freshdesk requires the domain name and the API key in order to authenticate the user. So we define two fields representing these two items and we define the label and tooltip that will appear in the form for each field. In this case, the auth definition will make Appmixer render a form like this:
The values configured by the user will be exposed in the context
object with the same keys as in the auth object. In this case, we will be able to access the values as context.domain
and context.apiKey
in our component NodeJS module.
While this field is optional, is recommended for a better UX since it is used to request the user profile information from a 3rd party API and together with accountNameFromProfileInfo
show the display name of the 3rd party account in Appmixer UIs.
See examples of real connectors that implement this method.
This field is the dot-separated path in the object returned by requestProfileInfo
that points to the value that will be used as the account name. Following the example, the object returned by requestProfileInfo
would have a structure like this:
We want to use the email to identify the Freshdesk accounts in Appmixer, so we set accountNameFromProfileInfo
as contact.email
.
If requestProfileInfo
is not defined, the auth
object will be used instead. The account name will be the resolved value for the property specified by accountNameFromProfileInfo
.
Similar to requestProfileInfo
. This property is used to validate if the authentication data entered by the user is correct. For this purpose you can call any endpoint that requires authentication, you can even use the same endpoint as requestProfileInfo
. If the data is correct, this function should resolve to any value. Otherwise, throw an error. You can also define validate
as an object. In that case, the object has the same structure as the object passed into the axios library. For example:
If the validate
function throws an exception and the exception contains message property, the message will be shown in the Connecting Account Failed page.
If validate
is specified as an object, the response error can vary from API to API. Appmixer will use internal heuristics to try to find the error message in the response object. If the error message is not shown on the Connecting Account Failed page, then the validate response can always be parsed and a proper exception thrown in the auth.js module using the validateErrCallback
function:
The password-based authentication is almost similar to key-based authentication as explained in the above section. The only difference is that you will use username
and password
inputs. The auth
property inside definition
will need to have two inputs as shown in the below-given code snippet. The validate
method is used to validate the input values provided. You can make API call by using the context.username
and context.password
properties. If the API you are validating returns a token, it will have to be returned from the validate method as shown below.
The pwd type can be used for the HTTP Basic Authentication. Here's a sample code:
Then the username and password is available in the components:
In order to use this mechanism, type
property must be set to oauth
. Here is an example from Trello components:
Note that in this case, the definition is a function instead of an object, but it still returns an object with the items needed for OAuth 1 authentication, similar to API key authentication. Now we explain the fields from the definition object:
Works exactly the same way as described in the API Key section.
This must be a function (or an object, or just a string URL as explained here) that returns a promise which must resolve to an object containing requestToken
and requestTokenSecret
the same way that is shown in the example above. These are needed to get the access token and become exposed by the context - context.requestToken
and context.requestTokenSecret
.
This must be a function (or an object, or just a string URL as explained here) that returns a promise which must resolve to an object containing accessToken
and accessTokenSecret
the same way that is shown in the example. Usually, you will be using the requestToken
and requestTokenSecret
inside this function, as they are required by the OAuth 1 flow in this step. Similarly to requestRequestToken
function, accessToken
and accessTokenSecret
will become exposed by the context - context.accessToken
and context.accessTokenSecret
.
Function, object or a string URL returning auth URL. Appmixer will then use this URL to redirect the user to the proper authentication page. The requestToken
is available in the context. The example shows the authUrl declaration using the token provided by the context.
Works exactly the same way as described in the API Key section.
This property serves the same purpose as validate property in the API Key mechanism. This is used by Appmixer to test if the access token is valid and accepted by the third-party app. You have access to context.accessToken
and context.accessTokenSecret
to make authenticated requests. If the token is valid, this function should resolve to any value. Otherwise, throw an error.
The latest OAuth protocol and industry-standard, OAuth 2.0 improved many things from the first version in terms of usability and implementation, while maintaining a high degree of security. It is easier to implement in Appmixer as well. In order to use this mechanism, type
property must be set to oauth2
. Here is an example from Asana auth.js
module:
The requestAccessToken
is used to get the access token while the refreshAccessToken
is used to refresh the access token later. Next, see the definition
object's properties explained in more detail:
Works exactly the same way as described in the API Key section.
Similar to OAuth 1, we should provide the authentication URL for the third-party app. However, due to the different authentication flows supported by OAuth 2, the way this is defined may vary according to the third-party implementation. If the OAuth 2 implementation is standard, you can define the authUrl
with just a string like:
Standard means, there is a response_type
parameter set to code
, the client_id
, redirect_uri
, state
and scope
parameters. If the OAuth 2 implementation requires any other parameters (or the standard ones use different names), then you have to define this property as a function and provide all the additional parameters. See, for example, the JIRA auth.js
module definition.
The same logic applies to the following property requestAccessToken
.
This function should return a promise with an object which contains accessToken
, refreshToken
(optional, some OAuth 2 implementations do not have refresh tokens) and accessTokenExpDate
or expires_in
(also optional if the implementation does not have tokens that expire). Inside this function, you should call the endpoint which handles the access tokens for the application. The following context
properties are available to you in this function: clientId
, clientSecret
, callbackUrl
and authorizationCode
. See, for example, the JIRA auth.js
module definition.
Works exactly the same way as described in the API Key section.
Part of the OAuth 2 specification is the ability to refresh short-lived access tokens via a refresh token that is issued along with the access token. This function should call the refresh token endpoint on the third-party app and resolve to an object with accessToken
and accessTokenExpDate
(and refreshToken
if needed) properties, as shown in the example. You have access to context properties clientId
, clientSecret
, callbackUrl
and refreshToken
.
Has the exact same purpose as the same method in the OAuth 1.
String or an array of strings.
String. The default one is ,
and you can change it to ' '
for example.
Sometimes the OAuth 2 needs a scope or a different scope delimiter. Here is a full example of the Microsoft authentication module with a different than the default scope delimiter:
By default, Appmixer will try to refresh the access token five minutes before its expiration. This is fine for most of the OAuth2 implementations, where an access token is usually valid for hours or days. However, there are OAuth2 implementations with stricter rules. With this property, you can define how many minutes (default, see refreshBeforeExpUnits) before the access token expiration should Appmixer refresh the token. Appmixer will not try to refresh the token before this value.
Works in cooperation with the refreshBeforeExp property. Useful if you need to go down to seconds. See the example above.
When you're developing an OAuth 2 application, at some point you have to register an app in the 3rd party system. For that, you need the redirect URI that points to the Appmixer API. The format of the redirect URI is https://[APPMIXER_TENANT_API_URL]/auth/[service]/callback
.
For example, if the service you're developing is called myService then the redirect URI will be https://[APPMIXER_TENANT_API_URL]/auth/myService/callback
.
The redirect URI can be changed per module in the Backoffice -> Connector Configuration page:
Context properties are different for each authentication type. But some of them are common for all types.
Wrapper around the axios library, making it easy to initiate HTTP requests without the need to import a 3rd party library:
Appmixer allows you to redefine the Connect Account button in the Designer and Integration Wizards. This is especially useful if the 3rd party has specific requirements for branding of the sign-in buttons, such as Google.
This is done with an optional connecAccountButton: { image: 'data uri' }
property. Example from the Google auth.js:
To set your OAuth applications secrets, follow the Connector Configuration guideline.
This tutorial shows you how you can implement your own custom webhook trigger component. The component will register its own webhook URL with a 3rd party API (possibly your own) and start accepting HTTP requests at that URL to further process and send to its output port. Moreover, we also show how to implement an API key based authentication for our component.
Throughout our example, we'll be using a non-existing Todo API service. This imaginary API provides the following endpoints and functionality:
Authentication: API key based authentication. All requests must provide the X-Api-Key
HTTP header that contains an API key that identifies a particular user.
POST /webhooks
HTTP endpoint with { url: SOME_URL }
payload to register a new webhook URL that will be called by the Todo API server every time there's a change in the user's todo list (new todo item, deleted todo item, updated todo item). This endpoint returns the webhook ID in its response ({ id: WEBHOOK_ID }
).
DELETE /webhooks/:id
HTTP endpoint that unregisters a previously registered webhook identified by its ID.
GET /me
HTTP endpoint that returns the user's profile.
Webhook callbacks: Our TODO API server calls registered webhooks every time there's a change in the user's todo list. The following events (webhook request payloads) are supported:
{ event: "todo-created", todo: { label: String, id: String } }
{ event: "todo-deleted", todo: { label: String, id: String } }
{ event: "todo-updated", todo: { label: String, id: String } }
Components in Appmixer are organized in a three level hierarchy: service, module, component. For example, a component that creates new rows in a Google Spreadsheet is organized as google.spreadsheets.CreateRow
. This hierarchy allows for sharing e.g. configurations or authentication mechanism between components by placing them either on the service or module level (in our example, all Google components use the standard Google OAuth 2 mechanism for authentication, therefore, the authentication module (auth.js
file) is inside the google/
directory). The component hierarchy is also reflected in the directory structure when you implement new services/modules/components. The directory/file structure of our demo component looks like this:
The top level directory is called tododemo
(our service), under which we have one module called core
and one component called NewTodo
.
The service directory contains two files, one that defines our authentication mechanism (remember our Todo API uses API key based authentication and we want to get the API key from the user) and one with our service manifest (service.json
) with metadata such as icon, label and description.
Our module (core
) contains just one component that resides the NewTodo
directory. The component is defined with two files, component manifest ( component.json
) and component behaviour (NewTodo.js
).
Let's now have a look at all the files in more detail.
The entire service.json
manifest for our connector looks like this:
The most important field in the service manifest file is the name
field. This field must follow the service naming syntax of the form [vendor].[service]
. In our case, we use appmixer
vendor name but you can create your own vendor too. Only keep in mind that to be able to publish a component with a certain vendor name, you must set this vendor name in the user profile via the Backoffice admin panel. See https://docs.appmixer.com/appmixer/appmixer-self-managed/installation#enabling-users-to-publish-custom-components for details. Our service name is simply tododemo
.
The rest of the fields mainly define how this service will be displayed in the UI. Once we publish our service to Appmixer, you'll notice a new connector in the left panel of the Designer UI:
As you can see, the metadata fields are displayed in the UI: the label
("Todo Demo"), description
("Appmixer Todo Demo Connector"), icon
(represented as a Data URI scheme instead of a URL link for better portability, see https://en.wikipedia.org/wiki/Data_URI_scheme) and the connector is displayed in the collapsible "Applications" category in the left panel.
The next file in our tododemo
directory is the auth.js
authentication module. This module defines how our users authenticate to our service. In our case, the Todo API requires API key based authentication so we want to collect the API key from the user when they use our NewTodo component in their flows/integrations:
Our auth.js
file looks like this:
As you can see, the authentication module is a NodeJS module that exports a JavaScript object with the definition of our authentication scheme. The top level type
field defines the type of the authentication scheme that Appmixer understands (see https://docs.appmixer.com/appmixer/component-definition/authentication#authentication-module-structure for details). In our case, we want to use the apiKey
authentication type. The definition.auth
section then describes the fields of the form that will be displayed to the user when they click on "Connect account", i.e. the API key that we want to collect from them. Note the key of each field is then used to reference the values in our component behaviour (see below). In our case, this is just one field with the key apiKey
.
The UI of the form looks like this:
As you can see, the form contains only one field with the label "API Key" (definition.auth.apiKey.name
) and tooltip "Your Todo app account ..." (definition.auth.apiKey.tooltip
). The type of the form field is text
.
The definition.validate
section of our authentication module tells Appmixer how to validate the user provided credentials (the api key in this case). The section can be defined as a templated HTTP request with url
, method
, headers
and data
fields (Appmixer actually uses the axios library to make the request so any axios request configuration field is supported: https://github.com/axios/axios#request-config). As you can see, you can use fields from the definition.auth
object by enclosing them with the {{
and }}
brackets. We take advantage of that by injecting the user provided api key in the X-Api-Key
HTTP header. Moreover, notice the url
field and the use of {{config.baseUrl}}
. The {{config.CONFIG_KEY}}
allow us to use service configuration values defined in the Backoffice admin panel. This is very handy if you don't want to hardcode certain values in your service/module/component definition and instead, make those values configurable via Backoffice. To do that, visit your Appmixer tenant Backoffice interface, go to "Services", click "Add" to add a new service configuration, provide the correct service ID in the form [vendor]:[service]
and add your custom configuration key/values by clicking on the "burger" icon:
At this point, our service definition is complete. We can start defining our NewTodo
trigger component. Note that our core
module does not have any definitions. In our example, we don't need to have separate modules under our service with their own definitions. We treat our entire service as a module. However, the core/
directory must still exist (or a directory with a different name that you choose).
Let's now have a look at the component.json
manifest file:
As you can see, the component manifest contains metadata about our component. Again, following the same convention, the name
of the component must be of the form [vendor].[service].[module].[component]
and it must follow the same directory structure our component lives in (core/NewTodo
). The author
field just contains the author of the component, possibly including their email address between <
and >
brackets. icon
field is again an image icon represented with the Data URI scheme. The webhook: true
field tells Appmixer to accept external HTTP requests on the component internal webhook URL (see below the context.getWebhookUrl()
function that gives us the component URL endpoint). The auth.service
field points to our authentication module and must be of the form [vendor].[service]
(or [vendor].[service].[module]
in case the auth.js
authentication module is defined under the module directory (which is not our case).
The outPorts
section defines the component output ports. In our NewTodo
trigger component, we only have one output port (out
) (In general, Appmixer supports an arbitrary number of output ports.):
The options
section of our out
output port then defines the variables that users can use in other connected components to reference output fields from our NewTodo
component. For example, let's say we want to send a Slack message for each new todo. We can create a flow that looks like the one below and use the variables from our component when composing the Slack message:
As you can see, the variables that show up in the variables picker are the ones that we defined in our options
section ("Todo ID" and "Todo Label"). The special "Raw Output" variable was added automatically by Appmixer to allow the user to use the raw JSON output of the component (which can be useful in some cases). Also note that the options
field is an array of objects with the label
and value
fields. The label
field simply defines the label of the output variable (e.g. "Todo Label"). The value
field references a key of the component output object, i.e. the object passed to the context.sendJson(obj, outputPort)
function in the first argument (see below the Component Behaviour section for more details).
The last missing piece to our demo Todo connector is the actual component behaviour, i.e. what the component actually does. The component behaviour is implemented as a NodeJS module. In other words, anything that you can implement in NodeJS can be a component in Appmixer (file conversions, business logic utilities, any API calls, ...). In our example, our NewTodo.js
file looks like this:
The module exports functions known to the Appmixer engine that the Appmixer engine calls at the right times. See Component Behaviour for more details: https://docs.appmixer.com/appmixer/component-definition/behaviour. For our example component, we export three virtual methods (naming convention for the exported functions):
receive(context)
that the Appmixer engine calls when the component receives an input (in our case, since our component does not have any input ports, the only input it can receive is from the component webhook URL, i.e. HTTP requests to the context.getWebhookUrl()
endpoint).
start(context)
that the Appmixer engine calls when the flow starts. This is the place where we initiate the HTTP request to the Todo API to register the component webhook URL so that our component is notified of changes in the user todo list (the actual changes are received in the receive()
method).
stop(context)
that the Appmixer engine calls when the flow stops. This is the place where we unsubscribe our webhook from the Todo API to properly clean up after ourselves (remove the webhook from the Todo API).
At this point, we have our entire service ready to be packed and published to our Appmixer tenant. To pack and publish services/modules, use the Appmixer CLI tool (https://docs.appmixer.com/appmixer/appmixer-cli/appmixer-cli). In short, the process is pretty straightforward and looks like this:
You should now be able to see the "Todo Demo" connector in the left panel of the Designer UI:
TIP: during debugging of your component, you'll be often re-packing/re-publishing the component and running a sample flow to see if it behaves correctly. Use the Log panel of the Designer UI to see the activity in your flow:
TIP 2: use the await context.log({ "foo": "bar" })
function to log any JSON object anywhere in your component behaviour file (our NewTodo.js). This allows you to print your own custom log messages in the Log panel.
You can download our sample service below. It's important to note that the service will not work without modifications since we used a dummy, non-existing Todo API. You should modify the service/component to fit your own needs and point it to your own API.
Build, edit and inspect individual flows in a comprehensive editor.
Set up a new instance with config
parameters and set
/get
methods:
config.el
...
Learn about widget
config
here.
config.flowId
Type: String
| Default: null
ID of a flow that is opened in the editor.
config.componentId
Type: String
| Default: null
ID of a component that is opened in the editor.
config.shareTypes
Type: Object
| Default: DefaultShareTypes
Override default sharing dialog types.
config.sharePermissions
Type: Object[]
| Default: DefaultSharePermissions
Override default sharing dialog permissions.
config.options.showHeader
Type: Boolean
| Default: true
Toggle visibility of the header.
config.options.menu
Type: Object[]
| Default: []
Add a dropdown menu input to trigger built-in and custom events:
The optional icon
property is a URL of an image or a base64
string.
config.options.toolbar
Type: Array[]
| Default: []
Add a toolbar with groups of built-in and custom buttons:
Specify Vue ComponentOptions
under widget
to create a custom toolbar button.
config.options.autoOpenLogs
Type: Boolean
| Default: true
Automatically open logs view when the flow is running.
config.options.triggerSelector
Type: Object
| Default: null
Automatically open trigger selector dialog when the flow has no trigger.
Learn about widget
instance here.
loader
Type: Boolean
| Default: null
Toggle a custom loading state.
error
Type: String
| Default: null
Toggle a custom error message.
stencilLayout
Type: String
| Default: 'default'
Sets the stencil panel layout to 'default'
(expanded) or 'collapsed'
.
validationLayout
Type: String
| Default: 'default'
Sets the validation panel layout to 'default'
(expanded) or 'collapsed'
.
flow:start
Toggle stage button to start the flow.
flow:stop
Toggle stage button to stop the flow.
flow:share
Click menu item to open sharing of the flow.
flow:rename
Click menu item to rename the flow.
flow:export-svg
Click menu item to export diagram of the flow to SVG.
flow:export-png
Click menu item to export diagram of the flow to PNG.
flow:print
Click menu item to print diagram of the flow.
flow:wizard-builder
Click menu item to open a wizard builder dialog.
component:add
Add a new component to the flow.
component:open
Open component inspector.
component:close
Close component inspector.
component:rename
Rename a component.
component:update-type
Use selection input to change component type.
navigate:validation
Click a button to show validation errors.
Browse logs of messages that passed through flows.
Browse and manipulate flows that are accessible to the current user.
Set up a new instance with config
parameters and set
/get
methods:
config.el
...
config.options
Type: Object
| Default: DefaultOptions
config.options.menu
Type: Object[]
| Default: []
Add a dropdown menu input to each flows to trigger built-in and custom events:
The optional icon
property is a URL of an image or a base64
string.
config.options.shareTypes
Type: Object
| Default: DefaultShareTypes
Override default sharing dialog types.
config.options.sharePermissions
Type: Object[]
| Default: DefaultSharePermissions
Override default sharing dialog permissions.
config.options.filters
Type: Object[]
| Default: []
Create dropdown inputs with built-in query filters:
config.options.customFilter
Type: Object
| Default: {}
Filter the flows with additional parameters:
This is especially useful in connection with customFields
metadata
to display multiple different Flow Managers each listing a different category of flows:
In Appmixer 6, the FlowManager widget is meant to display Automations only. These are regular flows, not the Integrations. This can be overwritten with the customFilter:
config.options.sorting
Type: Object[]
| Default: []
Create dropdown inputs with built-in sorting:
loader
Type: Boolean
| Default: null
Toggle a custom loading state.
error
Type: String
| Default: null
Toggle a custom error message.
layout
Type: String
| Default: grid
Change layout of the widget.
query
Type: Object
| Default: DefaultQuery
Set custom query parameters.
flow:open
Select a flow to open in Designer widget.
flow:create
Click Create Flow button.
flow:start
Toggle flow stage button.
flow:stop
Toggle flow stage button.
flow:clone
Click menu item to clone a flow.
flow:share
Click menu item to open sharing of a flow.
flow:rename
Click menu item to rename flow.
flow:remove
Click menu item to remove a flow.
Add menu item with flow:share
event for a configurable flow sharing dialog:
The Appmixer SDK uses this API module internally to connect to the REST API.
api.authenticateUser
Authenticate a user to Appmixer. Note that this can be a "virtual" user that exists for the sole purpose of associating a real user of your own product to a user in Appmixer. Each user in Appmixer can have a set of flows, can run and stop flows, and can see data going through their flows. The returned promise is either resolved with an object that contains a token (which you need to set with appmixer.set('accessToken', token)
to be able to make calls to the API backend. Or the promise is rejected with an error object. If the error object returns a 403 status code (i.e. err.response.status === 403
), the user does not exist in Appmixer.
api.authenticateWithEmailAndPassword
Similar to the api.authenticateUser
, but uses email instead of username.
api.signupUser
Create a new user in Appmixer. The returned promise is either resolved with an authentication object (containing the token
property) or rejected if the sign-up fails. If the email
parameter is not provided, then the username
will be copied as email.
api.createFlow
appmixer.api.createFlow(name, [descriptor], [properties])
Create a new flow in Appmixer. The returned promise resolves to the ID of the newly created flow. The properties
object can contain your own custom metadata inside the customFields
property. This is especially useful for filtering flows based on your own custom metadata.
api.deleteFlow
Delete an existing flow identified by flowId
.
api.getFlow
api.getFlows
Get all flows of the user or filter them by query
. query
is an object with the following properties: limit
, offset
, pattern
(a string to filter flows containing pattern in their names), sort
, projection
(allows you to exclude properties from the returned flow objects), sharedWithPermissions
and filter
.Example:
api.getFlowsCount
Get the number of all flows of the user or filter them by query
. query
is an object with pattern
property that can include a string to filter flows containing a pattern in their names. Example: { "pattern": "dropbox" }
.
api.updateFlow
Update an existing flow. update
can contain the following information: { flow, name, customFields }
, where flow
is the Flow Descriptor of the flow and customFields
is an object with your own custom metadata for this flow.
api.startFlow
Start a flow.
api.stopFlow
Stop a flow.
api.cloneFlow
Create a copy of an existing flow. The returned promise resolves to the ID of the newly created flow.
api.getUser
Get current user. The returned promise resolves to an object with username
.
api.getStores
Get all the data stores. The returned promise resolves to an array of stores each an object with name
and storeId
properties.
api.getStore
Get one store. The returned promise resolves to an object with name
and storeId
properties.
api.getStoreRecordsCount
Get the number of records in a store. query
is an object with storeId
and pattern
properties where pattern
is a string to filter records that contain the string in their keys or values.
api.getStoreRecords
Get store records. query
is an object with storeId
, pattern
(string to search for in keys/values), limit
, offset
and sort
properties. Example:
api.createStore
Create a new store. The returned promise resolves to the ID of the newly created store.
api.deleteStore
Delete a store.
api.renameStore
Rename an existing store.
api.createStoreItem
Create a new record in a store.
api.deleteStoreItems
Delete store items. items
is an array of objects each having a key
and storeId
properties identifying the item and store from which the item should be removed.
api.createAccount
Create a custom account.
api.getAccounts
api.getComponentAccounts
Get a list of accounts connected to a specific component.
api.getAccountFlows
Get a list of flows this account is used in. The returned promise resolves to an array of objects with flowId
and name
properties.
api.setAccountName
Rename a connected account. Note that this name is displayed in the Accounts widget and also in the Inspector UI of the Designer.
api.getLogs
Get logs. The query
is an object of the form { from, size, sort, query }
:
Get logs of a specific flow:
api.getLog
Get one log. logId
and index
are values returned from getLogs()
.
api.getPeopleTasks
Get all tasks of the user. query.role
can be either "approver" or "requester" and allows you to filter tasks based on the role. query.pattern
filters returned tasks by a term that must be contained in the task title. Settingquery.secret
to either the approverSecret
or requesterSecret
allows you to get a list of tasks of a different user for which you have the secret (other than the one identified by the access token, i.e. the currently signed-in user).
api.getPeopleTasksCount
Returns the number of tasks based on the query. See getPeopleTasks(query)
for more info.
api.getPeopleTask
Return one task identified by id
.
api.approveTask
Approve a task identified by id
. params
is an optional object that can contain the secret
property (approver secret). Having the secret allows you to approve a task of any user for which you have the secret, not just the currently signed-in user.
api.rejectTask
Reject a task identified by id
. params
is an optional object that can contain the secret
property (approver secret). Having the secret allows you to reject a task of any user for which you have the secret, not just the currently signed-in user.
api.getCharts
Returns all the Insights charts of the user.
api.getChart
Return one Insights chart identified by chartId
.
api.deleteChart
Delete an Insights chart identified by chartId
.
api.getFlowAuthentication
This request will return an object with all the components in the flow that have auth
section with all the available accounts.
error
The event is triggered when a request fails with an error or when the access token is invalid.
warning
The event is triggered when API validation fails with a warning.
Here's a how a full example of a component that sends SMS via the Twilio service can look like:
Defines how the component reacts on incoming messages.
Defines the component properties and metadata.
Our component uses the twilio
NodeJS library. Therefore, we need to list it as a dependency.
Metadata about the Twilio service. The Appmixer UI uses this information to display the Twilio app in the Apps panel of the Designer UI.
Defines the authentication for the Twilio service. This information is used both for rendering the Authentication dialog (displayed when the user clicks on "Connect New Account" button in the Designer UI inspector) and also for the actual authentication to the Twilio API and validation of the tokens.
Our auth.js
module uses the twilio
NodeJS library. Therefore, we need to list it as a dependency.
This is a helper component that is used to list the phone numbers registered in the user's Twilio account. You can see that this component is called in the "static" mode in the SendSMS component manifest. Note that this component is not used out of necessity but more as a convenience for the user. Thanks to this component, the user can just select a phone number from a list of all their registered phone numbers in the Designer UI inspector. An alternative would be to let the user enter the phone number in a text field. However, that might result in errors to the API calls to Twilio if the phone number does not exist in the user's list of registered phone numbers in their Twilio account.
Note the component is marked as "private" meaning that it will not be available in the Designer UI as a standalone component.
The numbers
output port of our helper component returns a list of numbers. However, when the component is called in a static mode from our SendSMS component Inspector UI, the expected value is a list of objects with label
and value
properties so that it can be rendered by the UI. Alternatively, we could have just skip the transformer altogether and use this array structure right in our context.sendJson()
call in our ListFromNumbers.js
file. The advantage of using transformers is that we can use the ListFromNumbers component as a regular component (i.e. not private
) and so allow the user to put the component in their flows. In other words, the same component can be used for static calls (to show a list of available phone numbers in the Inspector UI of the SendSMS component) as well as in a normal mode (as a regular component that can be put in a flow).
Create charts to visualize logs of messages that passed through flows.
Manage tasks created by utility components of flows.
Browse and manipulate charts created by the current user.
Set up a new instance with config
parameters and set
/get
methods:
config.el
...
loader
Type: Boolean
| Default: null
Toggle a custom loading state.
error
Type: String
| Default: null
Toggle a custom error message.
chart:clone
Clone chart.
chart:remove
Remove chart.
chart:open
Open chart in Chart Editor.
Manage accounts authorized by the current user.
Components receive incoming messages, process them, and generate outgoing messages. The way messages are processed is called component behaviour. It defines what components do internally and how they react to inputs.
Components are implemented as NodeJS modules that return an object with a set of methods (Component Virtual Methods) that the Appmixer engine understands. Let's start with a simple example, a SendSMS component that has one input port (message
), no output ports and its purpose is to send an SMS using the Twilio API.
As was mentioned in the previous paragraph, components are simple NodeJS modules that can implement a certain set of methods the Appmixer engine understands. The one most important method is the receive() method. This method is called by the engine every time messages are available on the input ports and the component is ready to execute. The method must return a promise that when resolved, acknowledges the processing of the input messages. If the promise is rejected, the Appmixer engine automatically retries to send the messages later using an exponential back-off strategy that prolongs intervals between the retries.
For trigger-type of components, the most important virtual methods to remember is tick() and start().
All virtual methods have one argument, the context
. The context object contains all the information you need to process your messages and send new messages to the output ports.
(applies to receive()
)
Incoming messages. An object with keys pointing to the input ports. Each message has a content
property that contains the actual data of the message after all variables have been resolved (replaced with actual data). For example:
Remember, if before running the flow, the input port message
was defined in the Inspector using variables:
where the flow descriptor would contain something like this:
the context.messages
object contains the result of replacing variables with actual data that was sent through the output port of the connected component, i.e.
Each message also contains the correlation ID in the context.messages.myInputPort.correlationId
property.
correlationId
is a "session ID" that associates all the messages in one pass through the flow. Every time a trigger component sends a message to the flow (e.g. webhook, timer, ...) and the message does not have a correlation ID yet, the Appmixer engine assigns a new correlation ID to the message. This correlation ID is then copied to all the messages that were generated as a reaction to the original trigger message.
Call this method to emit a message on one of the components output ports. The first argument can be any JSON object and the second argument is the name of an output port. The function returns a promise that has to be either returned from the receive()
, tick()
or start()
methods or awaited.
A convenient method for sending an array of objects to an output port. Note that this method does not send the entire array to the output port in one go but rather sends items in the array one-by-one to the output port. Therefore, your output port schema definition should contain the schema of the items of the array, not the array itself.
The authentication object. It contains all the tokens you need to call your APIs. The authentication object contains properties that you defined in the auth
object in your Authentication module (auth.js
) for your connector or implicit properties in case of OAuth (context.auth.accessToken
). For example, if our authentication module for our service (auth.js
) looks like this:
we can use the context.auth.accountSID
and context.auth.authenticationToken
in the component virtual methods to access the values for those properties that Appmixer requested from end-users when they authenticated to the connector:
When you configure your connector in the Backoffice, you can access the values in the context.auth
or context.config
objects. context.config
is an alias to the original context.auth
. This is especially handy for any configuration that you might want to have dynamically changed without the need to redeploy your connector with new configuration.
The configuration properties of the component. This corresponds to the properties
object from the component manifest file. For example, if our component defines the following properties in the manifest file:
context.properties.fromNumber
will contain the value the user entered in the Designer UI Inspector:
A persistent state of the component. Sometimes you need to store data for the component that must be available across multiple receive()
calls for the same component instance. If you also need the data to be persistent when the flow is stopped and restarted again, set the state: { persistent: true }
property in your component manifest, otherwise, the context.state
will be cleared when the flow containing the component stops.
context.state
is a simple object with keys mapped to values that are stored in the internal Appmixer database. This object is loaded on-demand in each receive()
call. It is not recommended to store large amounts of data here. Example:
The context.state
is especially useful for trigger-type of components when polling an API for changes to e.g. store the ID of the latest processed item from the API.
The context.state
object should not be used to store large amounts of data. The state is loaded with each received message on a component input port. The maximum limit is 16MB but storing such large objects will heavily slow down the processing of the component input messages.
Load the component's state from internal DB. Normally, you do not need to call this method explicitely since the component's state is loaded just before the component is triggered and the state is available in context.state
. However, there are cases when a component needs to reload its state from the DB where this function is useful.
Save an updated state object. See context.state
for details. The function returns a promise that resolves if storing of the state was successful.
Set a state key
to hold the value
. key
must be a string. value
can be any JSON object.
Get a state value stored under key
.
Remove a value under key
.
Clears the entire state.
Add value into set under key
.
Remove value from set under key
.
Increment value under key
. The second parameter is optional and can be used to set the increment value. The function return by default the new value (after incremented), if returnOriginal
is set to true, it will return the value before the increment.
Similar to the component state, this state is available to all components in the flow.
Load the state from the DB.
Set a state key
to hold the value
. key
must be a string. value
can be a string, number or a JSON object.
Get a state value stored under key
.
Remove a value under key
.
Clears the entire state.
Add value
into a Set stored under key
.
Remove value
from Set stored under key
.
Increment value under key
. The second parameter is optional and can be used to set the increment value. The function return by default the new value (after incremented), if returnOriginal
is set to true, it will return the value before the increment.
This is similar to the component state, but the service state is available across all components in the connector.
Load the state from the DB. The returned value is an array of state items each having the key
and value
properties, e.g. [{ key: "A", value: 1 }, { key: "B", value: 2 }]
.
Set a state key
to hold the value
. key
must be a string. value
can be anything that can be stored in Mongo DB.
Get a state value stored under key
.
Remove a value under key
.
Clears the entire state.
Add value
into a Set stored under key
.
Remove value
from Set stored under key
.
Increment value under key
. The second parameter is optional and can be used to set the increment value. The function return by default the new value (after incremented), if returnOriginal
is set to true, it will return the value before the increment.
Save a file to the Appmixer file storage. The function returns a Promise that resolves with the ID of the stored file ({ fileId }
). This is a more efficient and recommended version of context.saveFile(name, mimeType, buffer)
.
The structure of the returned object looks like this:
Replaces the content of the file. Returns a Promise with the ID of the file { fileId }
. The fileId remains the same.
Return object:
Returns a promise, which when resolved returns the file information (name, length, content type...).
Example return object:
Load a file from the Appmixer file storage. The function returns a promise that when resolved, returns the file data as a Buffer.
Read a file stream from the Appmixer file storage. The function returns a Promise, which when resolved, returns a NodeJS read stream that you can e.g. pipe to other, write streams (usually to a request object when uploading a file to a 3rd party API). This is a more efficient and recommended version of context.loadFile(fileId)
.
Remove a file from the Appmixer file storage. The function returns a promise.
Get a URL that you can send data to with HTTP POST or GET requests. When the webhook URL is called, the receive()
method of your component is called by Appmixer with context.messages.webhook
object set and context.messages.webhook.content.data
containing the actual data sent to the webhook URL:
Note: The context.getWebhookUrl()
is only available if you set webhook: true
in your component manifest file (component.json). This tells Appmixer that this is a "webhook"-type of component.
The full context.messages.webhook
object contains the following properties:
Send a response to the webhook HTTP call. When you set your component to be a webhook-type of component (webhook: true
in your component.json file), context.getWebhookURL()
becomes available to you inside your component virtual methods. You can use this URL to send HTTP POST or GET requests to.
When a request is received by the component, the context.messages.webhook.content.data
contains the body of your HTTP request. In order to send a response to this HTTP call, you can use the context.response()
method. See context.getWebhookUrl()
for details and examples.
Get the list of user's Data Stores.
Get value from the Data Store, stored under the key.
Set value to the Data Store under the key.
Remove the key from the Data Store.
Clear all data from the Data Store.
Find items in the Data Store.
Get a cursor.
Register Data Store webhook. If no events are specified, then the component will get all events from the Data Store. Possible events are insert, update and delete.
And the same functionality with registering only for the insert events.
Unregister a webhook.
Set a timer that causes the component to receive messageContent
in the receive()
method in the special context.messages.timeout.content
object. delay
is the time, in milliseconds, the timer should wait before sending the messageContent
to the component itself. This is especially useful for any kind of scheduling components.
The context.setTimeout()
function works in a cluster environment as opposed to using the global setTimeout()
JavaScript function. For example, a component that just slows down incoming messages before sending them to its output port, waiting e.g. 5 minutes, can look like this:
You can also access the correlation ID of the timeout message which can be useful in some scenarios. The correlation ID is available in the context.messages.timeout.correlationId
property.
The return value from this context method is a timeout Id (a UUID string). Each timeout has its own unique identifier. That can be used to clear the timeout.
Clear (cancel) a scheduled timeout.
Stop the running flow. Example:
The ID of the component.
The ID of the flow the component runs in.
The flow descriptor of the running flow. This allows you to access configuration of the entire flow within your component virtual methods. To get the configuration of the component itself, you can use context.flowDescriptor[context.componentId]
. Note that this is normally not necessary since you can access the properties of the component with context.properties
and the current input message with context.messages.myInPort.content
but it can be useful in some advanced scenarios.
This function lets you evaluate a JavaScript code in a sandbox. The first argument is the JavaScript code and the second is an object with data available to the code. The object is then available under $data
variable.
To support components that receive inputs and wait for some future asynchronous response to continue the flow execution from the state of the flow at the time the inputs arrived, Appmixer internally stores a data structure called Continuity scope. The Continuity scope is a document stored in the Appmixer internal DB and contains all the data the flow produced until it reached the component with the first message.
For example, some components receive an input message, call a 3rd party API, and wait for an asynchronous push-type of webhook call originating from the 3rd party API - that arrives at a later time - to receive at the component webhook URL. When the webhook arrives, the component produces a JSON and sends it to its output port. At this point, the component must have the state of the flow (all the related data from the same flow "run") to be able to resolve variables and continue execution.
The Continuity scope documents are deleted after a certain time (by default 100 days). If you need to have a component that can wait more than 100 days for the incoming webhook to resume the flow, you can use this function to adjust the timeout. The function excepts Date, number or a string
. If the argument is a number, it is considered to be the number of milliseconds from now (the time of the function call). If it is a string, it will be converted to Date
object using new Date(string)
function.
Load variables in your component. Variables are data available from components connected back in the chain. loadVariables()
returns a promise that resolves to an array that looks like this:
The array has as many items as there are other components connected to this component.
Example:
Log a message. The log message will be available to the end-users in the log panel of the Designer UI or in the Insights page of the Appmixer Studio. The argument has to be an object that can be stringified into JSON.
Example:
And the object can be seen in the log panel as:
lockName
string will be automatically prefixed with vendor.service:
. If a component type is appmixer.google.gmail.NewEmail
, the lockName will be prefixed with appmixer.google:
. This allows you to create a lock that is shared among all components within a service and prevents possible collisions between components from different vendors or services.
The first parameter is required, the second (options) is optional with the following optional properties:
ttl
, number, 20000 by default (ms)
retryDelay
, number, 200 by default (ms)
maxRetryCount
, number, 30 by default
Example:
Every function a component implements may throw an exception (or return a rejected promise).
Sometimes you, as a developer of a component, know that there is no point in retrying a message since no matter how many times Appmixer tries, the message will fail repeatedly. In such cases, you can tell Appmixer to cancel the message by throwing the context.CancelError(reason)
error object. This instructs Appmixer not to apply the auto-retry mechanism and the message will simply be discarded.
If a tick
function throws an exception, the exception will be logged (and visible in Insights). Appmixer will not apply the auto-retry mechanism since it assumes the developer handles the error manually inside the component code next time the tick()
function is executed.
Appmixer won't start a flow if any component in the flow throws an exception in the start
function. Such error will be logged and visible in Insights.
Appmixer will stop the flow even when a component in the flow throws an exception in the stop
function. Such errors will be logged and visible in Insights.
Learn about widget
config
.
Learn about widget
instance .
Learn about widget
config
.
Learn about widget
instance .
Get flow. The returned promise resolves to an object with the following information: { id, flow, name, stage, btime, mtime, thumbnail }
, where flow
is the Flow Descriptor, stage
is either 'running'
or 'stopped'
, btime
is the time the flow was created ("birth" time), mtime
is the time the flow was modified and thumbnail contains a thumbnail image (self-contained, in the format).
Get a list of connected accounts of the user. filter
is a custom query string (see the for an example). The returned promise resolves to an array of objects of the form { accountId, name, displayName, service, icon, profileInfo }
.
Learn about widget
config
.
Learn about widget
instance .
Learn about widget
config
.
Learn about widget
instance .
Learn about widget
config
.
Learn about widget
instance .
Learn about widget
config
.
Learn about widget
instance .
Messages that have been rejected 5-times are put in a special internal "dead-letter" queue and never returned to the flow for processing again. They can be managed and recovered using the Appmixer REST API.
This method has been deprecated. Use instead. Save a file to the Appmixer file storage. This function returns a promise that when resolved gives you a UUID that identifies the stored file. You can pass this ID through your flow (send it to an output port of your component) so that later components can load the file from the Appmixer storage using the file ID.
See, for example, the component for an example of how safeFileStream()
can be used.
This method has been deprecated. Use instead. Read a file stream from the Appmixer file storage. The function returns a NodeJS read stream that you can e.g. pipe to other, write streams (usually to a request object when uploading a file to a 3rd party API). This is a more efficient and recommended version of context.loadFile(fileId)
.
Since it is very common for components to initiate HTTP requests, Appmixer provides a convenient method to do so. The httpRequest
object/function is a wrapper around the well known library.
Call an Appmixer REST API endpoint. You can call any of the Appmixer endpoints defined in the . The main advantage of this method (as opposed to calling the API endpoint manually) is that the method automatically populates the "Authorization" header of the request to the access token of the user who owns the flow this component runs in. For example:
Flow properties are available in this object.
This method allows components to create a cluster lock. This is useful when creating a mutually exclusive section inside the component's code. Such a thing can be achieved in Appmixer using either (you can define a quota the way that only one receive
call can be executed at a time) or using locks. This method returns the lock
instance. Don't forget to call lock.unlock()
when you're done. Otherwise, the lock will be released after TTL.
If this function throws an exception, Appmixer will try to process the message that triggered this receive
call again later using an exponential backoff strategy. In total, Appmixer will try to process the failing message 30 times before it is saved into collection. Every unsuccessful attempt will be logged and visible in Insights.
api.set(name, value)
Set configuration property.
api.get(name)
Get configuration property.
api.on(event, handler)
Add event listener.
api.off(event, handler)
Remove event listener.
Virtual Method
Description
receive(context)
Called whenever there are new messages on input ports that the component is ready to consume. This method must return a promise that when resolved, tells Appmixer that the messages were successfully processed. When rejected, the engine retries to send the messages to the component again later.
tick(context)
Called whenever the polling timer sends a tick. This method is usually used by trigger Components to implement a API polling mechanism or for schedulers.
start(context)
Called when Appmixer signals the component to start (when the flow starts). This method is usually used by trigger components that might schedule an internal timer to generate outgoing messages in regular intervals or to register a webhook URL (context.getWebhookUrl()
with a 3rd party API).
stop(context)
Called when Appmixer signals the component to stop (when the flow stops). This is the right place to do a graceful shutdown if necessary. Webhook-based trigger components use this place to unregister their webhook URLs with 3rd a party API.
Property
Description
content.method
HTTP method of the request.
content.hostname
Hostname of the Appmixer API.
content.headers
HTTP headers of the request.
content.query
Object with query parameters, i.e. query string parsed into a JSON object.
content.data
Object with the body parameters of the request.
correlationId
A special ID generated by Appmixer that uniquely identifies the input message which resulted in generating the webhook URL. In other words, if you call context.getWebhookUrl()
in the receive()
method in a reaction to an input message that arrived on an input port of the webhook component, the correlationId
will be part of the returned webhook URL. This allows you to later associate the input message with the HTTP call to the webhook. A common pattern is to store the input message in the context.state
object and later use the context.messages.webhook.correlationId
to retrieve it back. For example, if you have an input port named myInPort
, you can get the correlationId
of the input message that just arrived by accessing the context.messages.myInPort.correlationId.
Appmixer UI is a tool for building user interfaces with component-based widgets.
Widgets are included in appmixer.ui
instances made with Appmixer constructor:
config.el
Type: String|Element
| Default: null
HTML DOM element to serve as a container of the widget.
config.theme
Type: Object
| Default: DefaultTheme
Custom theme definition.
config.l10n
Type: Object
| Default: DefaultL10N
Custom localization texts.
config.lang
Type: String
| Default: en
Language code for localization of components.
config.api
Type: Object
| Default: DefaultAPI
Custom API methods.
widget.open
Mount the widget
instance and render it inside the el
container.
widget.close
Unmount the widget
instance and hide the el
container.
widget.reload
Reload the entire widget
.
widget.reset
Reset the state of the widget to defaults.
widget.state
Use state
for properties that may change at any time when the widget
is active.
widget.set
Set config
property.
widget.get
Get config
property.
widget.on
Add a new event listener and disable the default handler of the event.
widget.off
Remove an event listener and enable the default handler of the event.
Browse apps and components that are accessible to the current user inside flows.
Set up a new instance with config
parameters and set
/get
methods:
config.el
...
Learn about widget
config
here.
Learn about widget
instance here.
loader
Type: Boolean
| Default: null
Toggle a custom loading state.
error
Type: String
| Default: null
Toggle a custom error message.
Appmixer SDK allows you to override API methods used by the SDK instance. This can be handy in edge case scenarios where you need to override the API requests and their parameters or response values.
Custom API is represented as an object composed of asynchronous methods that you set on your Appmixer SDK instance using the api
option:
The list of API methods can be found here.
An example how to redefine the flow update request.
Appmixer SDK allows you to change all the strings of all the UI widgets it provides (Designer, FlowManager, Insights, ...). This is especially useful to localize the entire Appmixer UI.
A strings object is represented as a JSON object that you set on your Appmixer SDK instance using the set('strings', myStrings)
method:
You can set the strings object anywhere in your application but usually, you'll do that right after initializing your appmixer instance. Note that you can even set the strings multiple times with different configurations in which case the Appmixer SDK will automatically re-render all the UI widgets using the configuration with new strings.
If you don't set strings, the default strings will be applied.
The strings object is a JSON object (with one exception, see below) that contains references to various UI elements within the Appmixer UI. The final values of the JSON objects are the actual strings used in the UI.
Example of setting the strings object:
For reference, we prepared a complete strings object for you to download and inspect to see all the possibilities for strings customization/localization.
For localization of time-related strings, a special time
root scope of the strings object can be modified:
Please download the default strings object above to see all the possibilities for time localization. Notice in the code above that there is one specialty to the time localization which (if used) makes the strings object non-JSON compliant. That's the ordinal(number)
function. Given a number, this function returns a string representing the number in ordinal form (i.e. 1 becomes "1st", 2 becomes "2nd", ...). Since this is hard to describe declaratively in JSON, the strings object may contain the oridnal(number)
function for you to be able to localize ordinal numbers. The default implementation looks like this:
Some text can contain both singular and plural versions based on whether the number variable used inside the text equals 1
or not. For example, the pagination widget in the Flows Manager:
The "of 198 flows" string used above can vary based on whether the total number of flows is more than one or if it equals one. The two versions can be expressed using the |
character in the strings object like so:
Also, notice the use of variables ({{total}}
in the example above). Variables are always enclosed by two curly brackets and are replaced by the SDK with the actual numbers when used. See the Appmixer default strings object for all occurrences of variables.
Customize UI widgets. You can change the colors, the typography, and much more.
To customize the UI widgets, you need to specify a theme
JSON object either in the Appmixer constructor:
and/or use the option with individual widgets:
If you wish to switch between themes, use the set("theme")
method, this will automatically re-render the UI applying your new theme:
The easiest way to change the overall styling is to use the theme variables
. The following example shows a complete list of variables that you can set in your theme to match your product branding:
Shapes of connectors in diagrams are customizable by choosing a preset in your theme.
Change the values of the entries to switch between presets. Here are built-ins per shape type:
action
action-vertical
action-dark
action-vertical-dark
trigger
trigger-vertical
trigger-dark
trigger-vertical-dark
selection
selection-vertical
selection-dark
selection-vertical-dark
Special version of actions and triggers that works better on dark backgrounds.
Special version of vertical actions and triggers that works better on dark backgrounds.
Charts (used in the Insights widgets - Insights Logs, Insights Chart Editor and Insights Dashboard) are customizable by a unique set of non-CSS properties. The values default to the current theme variables, except for colorway
. The colorway
option specifies the dynamic colors automatically picked by charts.
The theme JSON object references the entire Appmixer SDK UI in a complex tree of selectors. Elements use a hash symbol (#
) prefix and dynamic states use the at sign (@
). Each branch in the tree may hold nested selectors and any valid CSS properties for the element. The selectors are available for advanced customizations, but the structure may change between the Appmixer versions.
While the advanced theme styling gives you the most flexibility in customizing Appmixer UIs, we cannot guarantee that in some cases, the structure and nesting of the selectors might change between Appmixer versions. It is therefore recommended to use theme.variables
instead.
The numbers in the names of colors refer to a foreground opacity of the color over the base background color:
neutral96
is a foreground color with 96% opacity over the background neutral00
.
Some colors need a negative color NG
on top. For example, a white text on a blue button.
The numbers in size of the font refer to the defaults in pixels: size13
variable default is 13px.
For reference, we prepared a dark theme for all the Appmixer UI widgets that you can use as a quick overview of all the UI elements that you can set your custom theme for:
Screenshots of the dark theme for some of the UI widgets:
Appmixer lets you manage the components' inspector fields through the manifest or the strings object.
There are two ways how to customize component's strings:
Through a localization object inside the component's manifest
Adding custom strings to components namespace inside strings object
You can include custom strings inside the component's manifest using a localization object. The following is an example how to do it:
You can customize the component's label and description. You can customize the component's input/output port labels, the inspector input field labels, and output variables as well.
There's a slightly different specification when localizing output variables. As you can see in the example, after outPorts[0].options
the next path fragment is the option's value, instead of the index. This is because the component could have a dynamic output instead and different output variables can share the same index, so we use the value to specify them instead.
To switch the language in UI, you call the Appmixer instance set
method:
Note that if you want to customize the whole UI, you must use this in conjunction with the strings object. Here's an example:
The alternative way to customize the component's strings is using the Strings Object. There is a root namespace components
which contains all the custom strings definitions for components:
It follows the same pattern as in components, but we use the service/module path as a key for the definition:
Labels of groups of application modules can be localized/changed without rewriting a single module.json file.
Use the localization strings to do so:
When rendering the component's inspector, the strings are resolved with the following priority:
Localization object in the manifest (component.json).
Strings object components namespace.
Property in the manifest (component.json).
For example, when resolving an input's label, it will first look if there is a localization object in the manifest with a path to that input's label. If not, it will search the Strings Object. If none of that is defined, it will use values from the manifest.
Manage a flow that is used as an integration instance.
Set up a new instance with config
parameters and set
/get
methods:
config.el
...
Learn about widgetconfig
here.
config.flowId
Type: String
| Default: null
The ID of a flow that is opened in the wizard.
Learn about widget
instance here.
loader
Type: Boolean
| Default: null
Toggle a custom loading state.
error
Type: String
| Default: null
Toggle a custom error message.
flow:start
Submit the form and start the flow.
flow:validation
Flow validation errors changed.
cancel
Click a button to close the form.
close
Submit the form and wait for the flow to start.
Manage flows used as integration templates and instances.
Set up a new instance with config
parameters and set
/get
methods:
config.el
...
Learn about widget
config
here.
config.options
Type: Object
| Default: {}
config.options.customFilter
Type: Object
| Default: {}
Filter the integrations with additional parameters:
Learn about widget
instance here.
loader
Type: Boolean
| Default: null
Toggle a custom loading state.
error
Type: String
| Default: null
Toggle a custom error message.
integration:create
Click a button to to create a new integration from template.
integration:edit
Click a button to edit integration.
integration:remove
Click a button to remove integration.
integration:start
Click a button to start integration.
integration:stop
Click a button to stop integration.
Appmixer Backoffice is an administration UI for Appmixer. Use it to install, update and configure connectors, have an oversight of all the flows and users in Appmixer and set system configuration.
The Appmixer Backoffice is accessible via the "Admin" menu item in the Appmixer Studio:
Appmixer Self-Managed package is shipped as a zip archive and allows you to install the Appmixer platform on your own infrastructure or in a cloud-computing platform.
v18.15.0 (only for )
First, unzip the appmixer.zip
archive and change to the appmixer/ directory.
Now you can open the Appmixer Frontend in your browser at . Before you start creating flows with applications that require user authentication (OAuth), read this .
Stop Appmixer and remove all containers and images:
Copy the URL it gives you, in our case https://568284c4.ngrok.io
and replace the following line in the docker-compose.yml file in the root directory of the Appmixer package:
with the URL from ngrok:
Now restart Appmixer:
Or you can keep the docker-compose.yml file as it is and run it with:
But this command will set the GRIDD_URL to https://568284c4.ngrok.io
as well.
Users in Appmixer can have a special "admin" scope defined which gives them access to the entire system. Such users have access to all flows and all users and also access to the Backoffice UI application that gives these admin users an overview of users and flows in the system. Due to security reasons, the only way to give a user the "admin" scope is to modify a user record right inside the MongoDB database:
Replace the "admin@example.com" email address with an email address of an existing user which you want to grant admin rights. If you don't have a user created yet, please continue to the next "Getting Started" section to see how you can create one using the Appmixer Front-end UI interface. The command above should have the following output:
Once you have your admin user created. You can use the Backoffice UI to enable users to upload custom components. This can be done by setting the "Vendor" property on your users. Only users with the "Vendor" property set can upload components and only those components that have a matching [vendor] in their names. For example, component "appmixer.utils.forms.CreateForm" can only be uploaded by a user who has "Vendor" set to "appmixer".
Note that the Appmixer Trial package automatically sets the "appmixer" vendor on ALL newly created users so any user of the Trial package can publish custom components with no extra action required.
It is a good practice to set the "appmixer" vendor so that you can upload all of the connectors we provide without modifying all the component/service/module names:
From now on, my "david@client.io" user will be able to publish new custom components using the appmixer
CLI tool.
Appmixer offers a variety of system configuration options for advanced use cases, allowing you to finely tune the behavior of its underlying workflow and integration engine. To access and set these configuration options, navigate through the interface to the "System -> System Configuration" page.
Please be aware that certain configuration changes may not take immediate effect without restarting the Appmixer engine. For customers with a Self-Managed Appmixer installation, restarting the engine can be done at your convenience to apply the new settings. For those with a hosted Appmixer tenant, it's advisable to reach out to our support team at support@appmixer.com. Our team can provide guidance on how to effectively set these configuration options and assist with any necessary engine restarts to ensure your configurations are applied as intended.
Below is a list of available configuration options, accompanied by a brief explanation for each and their default values. These defaults are used by Appmixer in instances where no specific value is provided:
As you can see, there's a localization object at the end whose keys are language codes. This allows you to support multiple languages. Each value is an object whose keys are paths to the elements that will be customized (label, tooltip, placeholder, etc). The paths follow syntax.
Each key in components
object is the path to the component and the value is an object whose keys are the paths to elements (label, tooltip, placeholder, etc). This path follows the syntax. For more information about the Strings Object refer to the section.
Not only you can localize component's strings, but also services and modules. This allows you to change the label and description of the applications in the designer's left-side panel (the one you drag the applications from). To do it we can use either localization object in the service.json or module.json manifest or use the .
Some components require that the Appmixer engine URL is accessible on the Internet. This is especially true for any component that uses webhooks. In order to get these components working on your local machine, you need to set the APPMIXER_API_URL
environment variable on the Appmixer engine to point to a public URL. When using the Appmixer trial on your local machine, we recommend using the utility:
The information about automatically setting the GRIDD_URL is valid only when our docker-componse
.yml file is used. When you run Appmixer without it, the GRIDD_URL has to be set. This variable affects the .
As you can see, we have set the "scope"
property on our user record to ['user', 'admin']
. From now on, our user will have admin rights. Now you can sign-in to the Backoffice UI at using the email address and password of your admin user. If you visit the Users page, you should see something like this:
API_USER_CREATE_SCOPE
By default, the POST /user API is open to enable the sign-in feature for everyone. This option can restrict the access to this endpoint. It takes a list of scopes (comma-separated). If the value is not null, then a JWT token has to be used to call this API. Typically, the value is set to admin
.
null
APP_NAME
This will for example appear in the head title of a sign-in popup for Api Key services.
Appmixer
AUTH_HUB_AUTOMATIC
If the auth-hub system plugin is on and this value is true, all OAuth requests for unconfigured services will go through the Authentication Hub. It means that if you install Slack, for example, and do not configure the clientId and clientSecret the engine will use the Appmixer Authentication Hub for Slack authorization.
true
DEFAULT_USER_VENDOR
Vendor assigned to newly created users.
No value
AUTH_POPUP_DISPLAY_ERR
Whether to display validation errors from the authentication modules.
true
AUTH_POPUP_TIMEOUT_ERR
How many seconds before automatically closing the Connecting Account Failed popup window.
5
BROKER_MESSAGE_ACK_TIMEOUT
Timeout for message processing.
1500000
COMPONENT_FACTORY_TIMEOUT
An attempt to create a component will fail after this timeout.
300000
COMPONENT_RECEIVE_TIMEOUT
A message will be retried if the receive() function does not return within this timeout.
1380000
LIMIT_FLOW_UPDATE_BYTES
The max size in bytes of a flow descriptor to be able to be saved.
2097152
LIMIT_CC_ARCHIVE_MAX_BYTES
Maximum size in bytes for custom components.
10485760
LIMIT_WEBHOOK_BYTES
Maximum payload size in bytes for webhook components.
1048576
WEBHOOK_REQUEST_TIMEOUT
Timeout in milliseconds for webhook component requests.
10000
LIMIT_COMPONENT_STATIC_CALL_MAX_BYTES
Maximum size in bytes of the payload for component static calls.
104857600
PUBLIC_FILES_PREFIX
Public files (needed usually for domain verification) can be served from different paths. Path prefixes have to be separated by :
RETRY_BACKOFF
In case of an error, a message for a Component is rescheduled for another attempt. A back-off strategy is used. This value defines the number of attempts and the number of minutes between them.
DISPATCHER_PREFETCH_COUNT
The maximum number of Rabbit messages being dispatched at the same time.
500
INPUT_QUEUE_PREFETCH_COUNT
The maximum number of outgoing Rabbit messages waiting for aknowledgement at the time in the Input Queue. Subsequent incoming messages will not be sent until pending messages are aknowledged.
300
WEBHOOK_PREFETCH_COUNT
This is for webhooks from Appmixer to registered URLs. This is the amount of webhook messages that will be processing at a time.
50
WEBHOOK_RETRY_COUNT
Number of times that Appmixer will retry sending a webhook. Applies for all webhooks.
20
WEBHOOK_RETRY_INTERVAL
Initial interval in milliseconds for retries. Subsequent retries will take longer (multiplied by an internal factor).
30000
WEBHOOK_RETRY_MAX
Maximum interval in milliseconds between retries.
1800000
WEBHOOK_USER_CREATED
URL that will be called when new user is created (sign-up).
No value
WEBHOOK_FLOW_COMPONENT_ERROR
URL that will be called when a running flow encounters an error.
No value
WEBHOOK_FLOW_COMPONENT_ERROR_INCLUDE_QUOTA
Include quota errors among the errors sent to the registered webhook URL.
false
WEBHOOK_FLOW_COMPONENT_ERROR_INCLUDE_RETRY
Include retry attempts among the errors sent to the registered webhook URL. If false, the error will be sent, when all 30 attempts to process a message fail. If true, every failed attempt will be sent.
false
WEBHOOK_FLOW_STOPPED
URL that will be called when a flow is stopped due to an incompatible module upgrade.
No value
STRICT_COOKIES
If set to true, the engine will reject any incoming HTTP requests that have cookies that don't comply with the HTTP cookies RFC specification.
false
GARBAGE_COLLECTOR_CONTINUITY_SCOPES_TTL
The maximum time in days before continuity scopes are garbage collected. A continuity scope is a state of a flow including data from the flow runtime needed to continue the flow from a certain component onwards. For example, the Plivo.SendSMSAndWaitForReply sends an SMS and waits for an event (webhook) from Plivo that contains an SMS with a reply. The continuity scope contains all the data needed to continue the flow at a later time (when the webhook from Plivo is received which can take hours to days.
100
Appmixer can be installed in an AWS account via our public Terraform module.
Account in Appmixer Docker registry [registry.appmixer.com]. This account has been given to you during your sign-up process to the Appmixer Self-Managed package.
There are two types of installations:
Default installation
Custom installation
This type of installation assumes that you don't have any preferences regarding AWS Networking (VPC) and want leave all attributes in default. This kind of installation will create a new VPC and all required network parts (Internet Gateway, NAT Gateway, subnets, route tables, etc.). Then it will provision all the four required AWS managed services:
Amazon DocumentDB (MongoDB)
ElastiCache (Redis)
Opensearch (Elasticsearch)
Amazon MQ (RabbitMQ)
Next, it will provision AWS ECS (Elastic Container Service) with all Appmixer applications (Engine, Frontend, Backoffice, Quota and Logstash).
By configuring external_* values, you have a choice where to install the Appmixer stack and choose external services like:
external_vpc: your specific VPC
external_redis: external Redis (running either in AWS or somewhere else)
external_rabbitmq: external RabbitMQ (running either in AWS or somewhere else)
external_elasticsearch: external Elasticsearch (running either in AWS or somewhere else)
external_documentdb: external MongoDB (running either in AWS or somewhere else)
First, clone the Appmixer Module AWS git project with the Terraform code:
This repository contains examples for both dev and production use cases. Also, both of these examples include autoscaling configuration. Let's start with dev example:
This example defines a new VPC and it's CIDR block. You will need to configure:
zone_id (line #44) -> if set AWS Route53 will be used and you should also update aws_route53_zone (line #33) and set the domain name to be used.
ecs_autoscaling_config (line #52) -> Docker registry credentials, base64 encoded string.
input_init_user (line #54) -> here you can configure credentials for the initial user (Appmixer admin).
You should now be ready to provision the Appmixer stack.
In the ouptput of the above commands, you can see the exact resources that will be provisioned. If the output looks right we can proceed:
Again, you'll have a chance to check what exactly will be provisioned. If it looks correct, you can approve. After the Terraform run, you'll get the so called Terraform output, where you can find a lot of useful information like Loadbalancer name, service URLs, managed services, etc.
The Appmixer OpenAPI generator provides a tool to automatically generate Appmixer components from the OpenAPI v3 specification.
The tool generates an Appmixer connector from an OpenAPI specification. The spec can be extended with special x-connector-...
extensions to either provide details that the OpenAPI spec does not define (but are necessary for the Appmixer connector) or to make the resulting connector more user friendly (automatic pagination, dynamic select with options instead of providing hardcoded values, ...).
In the simplest form, you can run the generator by passing an OpenAPI spec as a parameter (both .json and .yaml file formats are accepted) together with a directory where the Appmixer connector will be generated:
Outputs:
The resulting connector is in the output directory:
This generates a complete Appmixer connector that can be packed and published to an Appmixer tenant:
If you use a 3rd party OpenAPI specification and want to make changes to it or enrich it with extensions, it would not be practical to edit this JSON/YAML file directly. Editing the file directly would make it hard to keep track of changes or update the original spec file when needed. To avoid direct editing of the spec, the Appmixer OpenAPI generator accepts a separate JSON Patch file (.json-patch) that defines changes to the OpenAPI spec. This way, the original OpenAPI spec can be left intact and only changes to it (together with extensions) can be defined separately.
The JSON Patch file follows the RFC 6902 (a quick reference) with some small modifications to make it easier to identify values that need to be changed in the OpenAPI spec:
The standard JSON Patch file uses JSON Pointer to identify values in a JSON file. However, the JSON Pointer format does not provide the expressive power that one would expect from a format that is to be manually written, especially when identifying multiple places within a JSON file using a single expression.
Consider the following example that removes the clientId
parameter from all the parameters
in the OpenAPI spec because the clientId
is already part of the authentication screens. In other words, we don't need this parameter to be part of the configuration of each Appmixer component (Inspector UI) since we got the value once when the user authenticated to the connector (created a Connection). Since our example OpenAPI spec includes the clientId
parameter in all the paramters
section for each path item, we want to remove all the occurences. If we were to use the standard JSON Pointer format, we would have to list all the occurences of the clientId
inside the parameters
section for each path item such as:
As you can see, this is not exactly user friendly. Instead, the Appmixer OpenAPI generator introduces the jsonpath
parameter for operations that accept the JSON Pointer-based path
parameter and therefore []allows us to use a single remove
operation instead:
The jsonpath
parameter accepts a JSON Path expression. The Appmixer OpenAPI generator pre-processes the JSN Patch file to expand operations that contain the jsonpath
parameter by finding all the values in the JSON OpenAPI spec document that the JSON Path expression points to and multiplying the operation for each value found while replacing the jsonpath
with path
containing the JSON Pointer expression that identifies the value.
To patch the OpenAPI spec with a JSON Patch file, pass the path to the patch file to the --patch
argument:
Sometimes, it is useful to be able to see byproducts of the preprocessing of the OpenAPI spec and the patch file. To generate these byproducts, use the --artifacts
argument:
This will create a special directory artifacts/
under the output directory with the following files:
checksum.json
contains a SHA-256 digests for all the files generated by the generator. This is useful for a quick check whether manual changes have been made to any of the generated files or whether a re-generation of the connector produced a different result (possibly due to changes in the OpenAPI spec).
openapi.json
is the original OpenAPI spec file.
openapi.normalized.json-patch
is the JSON Patch file withe "normalized" jsonpath
attributes. See Patching OpenAPI specification.
openapi.original.json-patch
is the original JSON Patch file.
openapi.patched.json
is the final OpenAPI spec, normalized, dereferenced (i.e. with resolved $ref
references) and possibly patched.
The Appmixer OpenAPI generator automatically checks for the operation success response JSON schema and if it detects type: array
, it adds an "Output Options" select box to the resulting generated component configuration. This allows the end-user to select, whether they are interested in outputting all items at once (array output) or one item at a time (object output). The same logic applies when the x-connector-pagination
extension is defined on the operation.
The following table describes how the OpenAPI specification fields/features are mapped to Appmixer connectors.
info.title
Service name (lower cased with spaces removed). Can be customized using x-connector-service
info.description
Service description
info.title
Bundle changelog version description
servers[0].url
The first server is used as the base URL for all HTTP calls in the connector. Other servers are ignored.
servers[0].variables
For apiKey
based authentication schemes, server variables are requested by the end-user in the authentication flow (i.e. considered in the auth.js
form and later used to construct the base URL). Only the first server variables are used. Other servers are ignored.
paths
Each Operation Object is converted to an Appmixer component. The path of the Path Object is used to construct the URL to be called (using the HTTP method from the Path Item Object) by the component as part of its behaviour.
paths...operationId
The operation ID is used as the Appmixer component name after normalization. The normalization consists of camel casing the operation ID if it contains the .
(dot) character since the dot character can't be used in the Appmixer component name. Otherwise, the operation ID is used as is. if the operation ID is missing in the OpenAPI spec, the Appmixer component name is constructed from the HTTP method and path (e.g. GET /user
) becomes GetUser
.
paths...responses
The first JSON-like success response object is heuristically found and used to construct the output of the component. Only one out
output port is always created and the response JSON schema is used as to construct the options
object of the output port (including JSON schemas for nested objects and arrays). The heuristics to find the first JSON-like response object finds the first response with 2xx
HTTP Status Code and Media Type containing the "json"
string.
paths...requestBody
The request body object is used to construct the input of the component (together with parameters
). Only the following media types are supported: applicatoin/json
, application/x-www-form-urlencoded
and multipart/form-data
. The input of the component is flattened and all nested properties of the original request body are delimited using the `'
paths...parameters
The parameters are used to construct the input of the component (together with requestBody
).
Input fields that are of the JSON schema type string
and format binary
are considered binary file inputs. These input fields are mapped to the Appmixer filepicker
inspector field type. For such inputs, the Appmixer OpenAPI generator automatically generates component code that loads the file from the Appmixer Files storage and streams it to the API endpoint. The multipart-form-data; boundary=FORM_BOUNDARY
Content-Type
HTTP header is automtically set on these requests.
When using an external OpenAPI spec that you do not want to edit, use the JSON Patch with the following operation to select OpenAPI operations that you want to convert to Appmixer components:
In the example above, only the meetings
and meetingCreate
operations will be considered by the OpenAPI generator for conversion.
Only OAuth 2 and API key based (with keys in all query/header/cookies) authentication is supported. The generated authentication module can be controlled with the x-connector-connection
, x-connector-connection-check
and x-connector-connection-profile
extensions.
The Appmixer OpenAPI generator accepts multiple extensions that can be used to define constructs that the standard OpenAPI specification does not provide. Without these constructs, the final generated connectors would not be as user friendly.
Description
Define the version of the connector. The connector is versioned using a major.minor.patch
versioning scheme. The major.minor
portion of the version string (for example 3.1
) shall designate the connector feature set. .patch
versions address errors in, or provide clarifications to, the connector, not the feature set.
Note that, unfortunately, we can't use info.version
as the connector version since info.version
can be an abitrary string while the connector REQUIRES the use of semantic versioning.
Location
Info Object
Value
A version string using the major.minor.patch
versioning scheme. If not provided, the default value is 1.0.0
.
Example
Description
Define an icon for the connector.
Location
Info Object
Value
A URL to an image (png, jpeg or SVG). A Data URI is also accepted.
Example
Description
Alternatively to the x-connector-icon
, the Appmixer OpenAPI generator also accepts the more common x-logo
extension.
Location
Info Object
Value
Either a URL to an image (png, jpeg or SVG; with data URI also accepted) or an object with a url
field that points to an image.
Example (URL directly)
Example (nested url field)
Description
Define a name for the Appmixer connector service of the service/module/component hierarchy.
Location
Info Object
Value
A string. If not provided, the default value is the info.title
in lower case with removed spaces.
Example
Description
Define a name for the Appmixer connector module of the service/module/component hierarchy.
Location
Info Object
Value
A string. Deault value is core
.
Example
Description
An HTTP request that will be called to validate whether the user authentication credentials (Connection) are valid. In the most common sense, this request is called to check whether the api key or OAuth access token is valid. Typically, this is a call to a /me
-type of endpoint that is sent with authentication details included (OAuth access token, api key, ...). The check is valid if the response is a success (HTTP 2xx status code).
Location
Security Scheme Object
Parameters
method
... HTTP method (GET, POST, ...)
url
... The endpoint to be called. If the URL is relative, it will be relative to the base URL (defined in the OpenAPI servers
section).
headers
... HTTP headers object.
query
... HTTP query object (key-value pairs representing the query string parameters).
The values of the url
, headers
, and query
parameters can contain parameters enclosed in curly braces. These parameters will be replaced by the values collected from the user during the authentication flow (the named parameters in the Security Scheme Object) or are implicitely provided (accessToken
in case of OAuth).
Examples
A common use in API keys-based authentication:
A common use in Oauth-based authentication:
Description
An HTTP request that will be called to get the user profile (used mainly to get a display name for the Connection). Typically, this is a call to a /me
-type of endpoint that is sent with authentication details included (OAuth access token, api key, ...). The returned value is then used to get the display name for the Connection. Alternatively, it can also be a string that can contain parameters in curly braces. For security schemes with "type": "apiKey"
, the {apiKey}
paramater will be replaced with the API key provided by the user.
Location
Security Scheme Object
Parameters
method
... HTTP method (GET, POST, ...)
url
... The endpoint to be called. If the URL is relative, it will be relative to the base URL (defined in the OpenAPI servers
section).
headers
... HTTP headers object.
query
... HTTP query object (key-value pairs representing the query string parameters).
transform
... JSONata expression to retrieve the value from the response payload that will be used as a display name for the Connection.
The values of the url
, headers
, and query
parameters can contain parameters enclosed in curly braces. These parameters will be replaced by the values collected from the user during the authentication flow (the named parameters in the Security Scheme Object) or are implicitely provided (accessToken
in case of OAuth).
Examples
A common use in API keys-based authentication:
A common use in API keys-based authentication with no /me endpoint:
A common use in Oauth-based authentication:
Description
For API-key based authentication, the x-connector-connection
extension is an alternative to the OpenAPI-native definition. The x-connector-connection
is more compact and contains all the necessary parameters for the entire Connection definition (including checks and profile requests). Also, the standard OpenAPI apiKey
security scheme does not have a mechanism to describe how exactly is the api key passed in the request. It only allows to define where it is passed (header, query). However, some APIs require the value of the api key parameter to be more than just the key. For example, the OpenAI API uses the following in the HTTP headers: { Authorization: "Bearer {apiKey}" }
. The Voys API even combines more api keys into one with: { Authorization: "token {username}:{apiKey}" }
. If the x-connector-connection
extension is used, it overrides other authentication definitions from the security
section.
Location
Root.
Parameters
type
... The type of the authentication scheme. Only "apiKey"
is currently accepted.
in
... The location of the authentication credentials (api key(s)) in all authentication requests. Can be "header"
, "query"
or "cookie"
.
name
... Name of the header, query parameter or cookie.
value
... The template for the value of the authentication credential. It can use any property from the schema
object enclosed in curly brackets. These will be replaced with real values at runtime.
schema
... JSON schema describing the authentication parameters. The schema is used to generate the authentication screen for the user where the parameter values will be requested from the user in a HTML form. Only a flat object type of JSON-schema structure is supported at this point and only the string
type is supported for the properties.
check
... An HTTP request that will be called to validate whether the user authentication credentials (Connection) are valid. The same parameters as in the x-connector-connection-check
extension are supported.
profile
... An HTTP request that will be called to get the user profile (used mainly to get a display name for the Connection). The same parameters as in the x-connector-connection-profile
extension are supported. Alternatively, the profile
can also be a template string that can contain parameters defined in the schema
in curly brackets.
Examples
Two API keys (one username, one api key, custom value for Authorization header)
One API key (custom value for authorization header)
Description
A custom label for the generated Appmixer component. By default, the label of the component is the operationId
defined in the Operation Object.
Location
Operation Object
Value
A string.
Example
Description
A custom description for the generated Appmixer component. By default, the description of the component is of the form <label>summary</label></br>description
.`
Location
Operation Object
Value
A string.
Example
Description
In OpenAPI specification, parameters
and requestBody
fields are typically not ordered by their importance from the perspective of a user filling a form that contains those fields. Therefore, in many cases, required fields or important fields may end up at the bottom of such a form. This is obviously not a great user experience. The x-connector-field-index
allows you to give fields an order that you'd prefer when the fields are rendered in a form for the user to configure (Appmixer Inspector panel).
Location
Schema Object
Value
An integer.
Example (parameters)
Example (requestBody)
Description
Since not all options can be defined using the JSON schema, the x-connector-field-options
makes it possible to add additional options to the generated Appmixer inspector field. Also note that the field options are processed after all other heuristics used to automatically convert JSON schemas to Appmixer inspector field took place. Therefore, if the generated inspector field is not what you expect, you can use the x-connector-field-options
to override the generated setting.
Location
Schema Object
Value
An object.
Example
Description
Location
Request Body Object
Parameters
language
... the language of the expression. Currently only JavaScript is supported.
expression
... the expression to transform the request body. The request body is available with the requestBody
variable inside the expression. For example, the expression requestBody = {}
empties the request body object completely, ignoring everything the user has set in the inspector.
Example
Description
Turn a property into a select box with options loaded from another source.
Location
Schema Object (Any property in either requestBody
or parameters
).
Parameters
operationId
... the ID of the operation within the OpenAPI spec that will be called to retrieve the data for the options of the select box. The resulting data will be read from the out
output port.
transform
... A JMESPath expression that must transform the resulting data into an array of objects with value
and label
fields. The label
field is the visible name of the option to the user. The value
field is the value that what will be used as the value for the field for which the x-connector-source
extension is defined. [Note that JMESPath is used instead of JSONata - prevalent in other extensions - since the Appmixer tranform functions cannot be currently asynchronous.]
parameters
... [optional] A map of parameters that will be propagated to the source operation. An object with keys representing parameters of the source operation and values that are JSON pointers pointing to data of the operation that will propagate to the source operation. The root of the JSON pointer points to the operation under which the x-connector-source
extension is defined.
requestBody
... [optional] A map of request body parameters that will be propagated to the source operation. An object with keys representing request body parameters (nested parameters are expressed using the /
character) of the source operation and values that are JSON pointers pointing to data of the operation that will propagate to the source operation. The root of the JSON pointer points to the operation under which the x-connector-source
extension is defined.
Example (typical use)
Consider an OpenAPI specification for Connectwise (examples/openapi/connectwise/openapi.json
) for an endpoint that updates a service ticket (putServiceTicketsById
). This endpoint takes board.id
parameter in the request body to identify the service board the ticket should be placed in. If we left the OpenAPI spec without any modifications, the generated component would request the board.id
from the user on an as-is basis, i.e. it would ask the user to provide the ID of the service board in a text input field. As you can imagine, this is not very helpful to the user since the user does not know that value. One way for the user to get around this would be to use another component such as getServiceBoards
to search all the service boards and use the id
output of the board found as an input of our board.id
. However, in this case, it is much more user friendly to just show a list of service board names to the user right inside our putServiceTicketsById
component and use the selected board's id
automatically without the user having to deal with plain IDs.
Example (parameters propagation)
Sometimes, the source operation needs some parameters that are defined by the user in the design phase. To specify which parameters are propagated to the source operation, define the parameters
and requestBody
sections. In the example below, the userId
is a parameter of the source operation and { form: { type: ... } }
is part of the request body of the source operation. Assume that both parameters are required and without which the source component cannot return any results (and fails for missconfiguration). We can simply provide a mapping of the parameters and request body data that will be propagated to the source component like this:
Description
Define a pagination method. This is especially useful for components that call endpoints that return an array of values (as opposed to a single object). In such cases, we may want to let the user configure how many items to return. At the same time, this limit on the number of items can be higher than the endpoint allows to return in one call (i.e. higher than the common limit
paramater often used in the offset-limit type of pagination). If the x-connector-pagination
is defined, the generated component will be smart enough to call the endpoint with varying parameters multiple times to retrieve the desired number of items.
Location
Operation Object
Parameters
type
... the type of the pagination used. One of "page"
, "cursor"
, "link-header"
and "once"
.
parameters
... the parameters of the pagination specific to each type.
page
type pagination parameters
offset
... The name of the query parameter of the 3rd party endpoint that represents the (zero-based) offset of the first item returned in the collection.
limit
... The name of the query parameter of the 3rd party endpoint that represents the maximum number of entries to return.
page
... The number of items to return in one call of the endpoint. This number will be added to the offset
parameter whith each subsequent call of the endpoint.
results
... A path to the data returned from the endpoint that represents the array of items. This path can point to nested properties by using the dot character to separate levels. For example content.items
.
count
... [optional] A JSONata expression that is evaluated on the response data that returns the total number of items in the collection. This is a hint to the component to stop calling the endpoint when all results have been received. The most common value just points to the field from the response data that contains the total count of items. Example: resultSet.count
.
more
... [optional] A JSONata expression that is evaluated on the response data that returns a boolean value that tells teh component to stop calling the endpoint in order to get more results. In other words, if more
evaluates to false, the component knows it has collected all the results. The most common value points to the boolean field from the response data that contains informatoin on where there are more items. Example: hasMore
.
cursor
type pagination parameters
limit
... The name of the query parameter of the 3rd party endpoint that represents the maximum number of entries to return.
page
... The number of items to return in one call of the endpoint.
results
... A path to the data returned from the endpoint that represents the array of items. This path can point to nested properties by using the dot character to separate levels. For example content.items
.
next
... A JSONata expression that is evaluated on the response data that should return the value of the cursor, i.e. the next cursor. This is typically the ID of the next item starting from which we want retrieve the next batch of results.
cursor
... The name of the query parameter of the 3rd party endpoint that represents the cursor.
link-header
type pagination parameters
This pagination expects the response to contain the Link
HTTP header that contains at least one URL that, if requested, returns the next batch of items. This URL must have the rel="next"
parameter set. For example:
limit
... The name of the query parameter of the 3rd party endpoint that represents the maximum number of entries to return.
page
... The number of items to return in the first call of the endpoint. (Subsequent calls are assumed to have this parameter automatically added by the server returning the next URL in the Link
HTTP header.)
once
type pagination parameters
This special type of pagination makes it easy to introduce paginated "features" (Limit inspector field, result
output variable) on endpoints that do not support pagination but return an array of items. This is typically the case for endpoints that, even though return an array of items, do not return "much" of the items so no pagination is defined and necessary.
results
... A path to the data returned from the endpoint that represents the array of items. This path can point to nested properties by using the dot character to separate levels. For example content.items
.
Description
Define a webhook trigger. Two common types of webhooks are supported:
A "subscription" type of webhook requires the 3rd party API to provide two endpoints for dealing with webhooks: subscribe and unsubscribe. The "subscribe" endpoint accepts a URL that will be called by the 3rd party to notify of new events occuring. The "unsubscribe" endpoint makes it possible to tell the 3rd party to remove the subscribed webhook from its registry and therefore stop receiving events on that URL.
A "static" type of webhook allows to manually register a global webhook URL with the 3rd party that the 3rd party will notify of new events occuring. In this case, both the subscribing and unsubscribing of the webhook is done manually (usually as part of the OAuth app configuration).
Location
Path Item Object of the webhooks
section.
Parameters
type="subscription"
webhook parameters
type
... The type of the webhook. For the subscription type, use "subscription"
.
subscribe
... An object defining the HTTP request to be sent to subscribe a webhook with the 3rd party to receive events to.
subscribe.url
... The URL of the request.
subscribe.method
... The HTTP method of the request.
subscribe.headers
... The HTTP headers of the request.
subscribe.body
... The data payload of the request.
unsubscribe
... An object defining the HTTP request to be sent to unsubscribe a webhook from the 3rd party in order to stop receiving events to the previously subscribed URL.
unsubscribe.url
... The URL of the request. A special template parameters can be used to
unsubscribe.method
... The HTTP method of the request.
unsubscribe.headers
... The HTTP headers of the request.
unsubscribe.body
... The data payload of the request.
outputCondition
... [optional] A JSONata expression that must evaluate to true in order for the trigger to output data in reaction to an incoming event that arrives at the webhook URL.
outputTransform
... [optional] A JSONata expression that can be used to transform the output of the trigger. The expression evaluates on the incoming request payload.
Special placeholders can be used in the url
, headers
and body
fields of the subscribe
and unsubscribe
parameters. These will be replaced at runtime and include:
{$baseUrl}
... This will be replaced by the base URL of the 3rd party service (see the servers
section of the OpenAPI specification). Usable both in subscribe
and unsubscribe
objects.
{$webhookUrl}
... The Webhook URL of the trigger component. Usable both in subscribe
and unsubscribe
objects.
{$request.body}
... The entire request body. See OpenAPI runtime expressions for more details. Usable mainly in the output
parameter.
{$response.body#/foo/bar}
... A portion of the response body specified by a JSON Pointer. See OpenAPI runtime expressions for more details. Usable mainly in the unsubscribe
object to point to fields from the payload returned in the response to the subscribe endpoint call. Typically, the response contains the ID of the webhook which we want to use in the request to unsubscribe from the webhook later on.
{$response.transform#JSONATA_EXPRESSION}
... The response transformed using the JSONata expression language. This gives you the most expressive power - if needed. Sometimes, the {$response.body#/foo/bar}
that uses the JSON Pointer does not have enough expressive power to extract the ID of the webhook to unsubscribe. In these cases, use this parameter together with a JSONata expression to query the data you need to unsubscribe the webhook.
{$response.header.header_name}
... The value of the specified response header. See OpenAPI runtime expressions for more details.
{$parameters.parameter_name}
... The value of the specified parameter from the parameters
section of the OpenAPI operation. parameters
make it possible to parametrize the webhook trigger (request information from the user). Use the {$parameters.parameter_name}
placeholder to reference the parameter values in both the subscribe
and unsubscribe
objects.
{$connection.profile#/foo/bar}
... A portion of the connection profile object (see x-connector-connection-profile
). This is especially useful to get values for data such as user ID, account ID or other, that are returned from the /me
-type of endpoint. Use JSON Pointer after the #
character to get to any nested values.
Example: using of parameters, output transformation
Example: a more complex unsubscribe URL with transforms
type="static"
webhook parameters
type
... The type of the webhook. For the static type, use "static"
.
path
... The endpoint of the static webhook. The entire URL will be {APPMIXER_API_URL}/plugins/appmixer/{SERVICE}{PATH}
. For example: https://api.appmixer.com/plugins/appmixer/zoom/events
.
pattern
... A JSONata expression that is evaluated on the incoming webhook request (with payload
, headers
, method
and query
fields). If the topic
parameter matches the evaluated pattern
, the component triggers and outputs the incoming request payload (considering outputCondition
is met).
topic
... The value of the evaluated pattern
for which the component is interested in receiving events.
outputCondition
... [optional] A JSONata expression that must evaluate to true in order for the trigger to output data in reaction to an incoming event that arrives at the webhook URL.
outputTransform
... [optional] A JSONata expression that can be used to transform the output of the trigger. The expression evaluates on the incoming request payload.
crc
... The definition of the Challenge-Response check. Some APIs use the challenge-response check to confirm the ownership and the security of the webhook notification endpoint URL. In this scenario, the API sends a POST request to the webhook URL with a challenge (a token) and the webhook endpoint must return the challenge hashed with a secret token known to both sides.
crc.condition
... A JSONata expression evaluated on the incoming request (with payload
, headers
, method
and query
fields) that must evaluate to true for the incoming request to be considered as the challenge request (i.e. to ignore all non-CRC requests).
crc.alg
... The hash function used to calculate HMAC. Typically "sha256"
.
crc.key
... The name of the configuration value of the connector that contains a secret token known to both sides, that will be passed to the HMAC to calculate the final token for the response. This points to the configuration key that can be set in the Appmixer Backoffice -> Configuration for the service appmixer:SERVICE
.
crc.challenge
... A JSONata expression evaluated on the request returning the challenge token (that will be used together with the key
to calculate the HMAC).
crc.digest
... The encoding of the HMAC. Supported values are "hex"
, "base64"
and "utf8"
.
crc.response
... A JSONata expression evaluated on an object of the form { responseToken, challenge }
containing both the original challenge
and the final calculated HMAC (responseToken
). The expression should return the payload to be send as a response to the CRC request.
cursor
type pagination parameters
limit
... The name of the query parameter of the 3rd party endpoint that represents the maximum number of entries to return.
page
... The number of items to return in one call of the endpoint.
results
... A path to the data returned from the endpoint that represents the array of items. This path can point to nested properties by using the dot character to separate levels. For example content.items
.
next
... A JSONata expression that is evaluated on the response data that should return the value of the cursor, i.e. the next cursor. This is typically the ID of the next item starting from which we want retrieve the next batch of results.
cursor
... The name of the query parameter of the 3rd party endpoint that represents the cursor.
Description
Define a URL base path for any link with a relative URL in the connector. Applies only to relative URL links in markdown formatted descriptions. When provided, it will convert [link text](/documentation#endpoints-price-range)
to <a href="https://www.boredapi.com/documentation#endpoints-price-range">link text</a>
.
Location
Info Object
Value
A string.
Example
A minimalistic OpenAPI spec for the single-endpoint https://www.boredapi.com/ API.
No JSON patch, no OpenAPI extensions are used.
OpenAPI Spec
A custom written single OpenAPI spec with extensions right inside the OpenAPI document.
Pagination, connection check/profile, source, custom API-key based authentication, webhooks.
OpenAPI Spec
Used extensions:
x-connector-icon
x-connector-service
x-connector-module
x-connector-webhook
x-connector-source
x-connector-pagination
x-connector-connection-check
x-connector-connection-profile
OpenAPI with a separate JSON Patch file with changes and fixes, separate OpenAPI spec for webhook definitions externally referenced from the OpenAPI spec.
File upload (multipart/form-data), OAuth 2, pagination (cursor), inspector field index, request body JavaScript transformation.
OpenAPI Spec
OpenAPI Webhooks Spec
JSON Patch
Used extensions:
x-connector-icon
x-connector-service
x-connector-module
x-connector-webhook
x-connector-pagination
x-connector-connection-check
x-connector-connection-profile
x-connector-field-index
x-connector-transform
OpenAPI in YAML format with a separate JSON Patch file with changes and fixes.
File upload (multipart/form-data), custom API key-based auth, source, pagination (once), inspector field index.
OpenAPI Spec
JSON Patch
Used extensions:
x-connector-icon
x-connector-service
x-connector-module
x-connector-pagination
x-connector-connection
x-connector-field-index
x-connector-source
All versions starting from version 2 (Swagger) is supported, i.e. 2.0
, 3.0.x
and 3.1
. Note that the 2.0
version is automatically detected and a conversion to version 3.0.x
is automatically performed using the swagger2openapi npm package.
Also note that the tool accepts both YAML and JSON files as inputs for the OpenAPI specification.
Fields in parameters
and requestBody
are mixed together to form a flat list of configuration fields of the connector. Conflicting names are not accepted at this point.
Only the first subschema of the oneOf
JSON schema composition keyword is used.
allOf
subschemas are merged into one using the (json-schema-merge-allof)[https://www.npmjs.com/package/json-schema-merge-allof] library.
Only the first subschema of the anyOf
JSON schema composition keyword is use.d
Only the first subschema of the oneOf
JSON schema composition keyword is used.
The output JavaScript code is formatted using (ESLint)[https://eslint.org/].
Appmixer flows can contain custom metadata specific to your application. This is especially useful for filtering flows and displaying different flows for different use cases.
Each flow can have assigned a custom metadata JSON object the customFields
field. In the examples below, we show how to define custom metadata on a flow using each of the available Appmixer interfaces (REST API, Appmixer SDK).
Use the customFields object when creating new flows:
... or when updating existing flows:
Create new flows with customFields
:
... or update existing flows with:
Now when you have flows with your own custom metadata defined, you can use filters to search for flows based on values of your metadata.
Moreover, you can use the following operators in your filter queries: !, ^, $, ~, >, <, $in
. For example, to filter flows that do not fall into our "healthcare" category, we can use:
The main power of custom metadata is that you can display multiple FlowManager UI instances on your pages for different categories of flows. A typical example is to have a custom field "template" and split all flows in Appmixer to "template" flows and actual flow instances. You can achieve that by having two lists of flows in your UI displayed with the FlowManager UI widget, one for flow templates and one for actual flow runs. The flow templates would only display flows with "template:true"
filter and the other list will display flows with "template:!true"
filter. Based on where the user actually created the new flow, you would assign either template:true
or template:false
custom field to the flow object.
Use the customFilter
option when creating an instance of your flow manager. The FlowManager widget will then use this filter whenever requesting list of flows to display from the API:
You can have another instance of FlowManager with non-template flows:
Appmixer features an internal Authentication Hub, functioning as an authentication proxy, which simplifies the setup process by eliminating the need to register your own OAuth credentials with third-party services. This means all OAuth-based connectors offered by Appmixer are ready to use right out of the box. However, should you prefer to use your own OAuth credentials for enhanced customization or compliance reasons, you have the flexibility to do so. This can be done for all or selected connectors by specifying your OAuth credentials (clientId
and clientSecret
) as outlined in the Connector Configuration section of this documentation.
The Authentication Hub is enabled for all hosted Appmixer tenants. In self-managed installations, the Authentication Hub has to be explicitly set.
To start using the Authentication Hub, the system plugin auth-hub
has to be turned on. This is automatically turned on for Hosted Appmixer tenants and can be turned on by adding the auth-hub
plugin ID to the SYSTEM_PLUGINS
ENV variable.
To request the AUTH_HUB_TOKEN
, contact our customers support by sending email to support@appmixer.com.
You can redirect all OAuth requests to the Authentication Hub, or you can use it just for a selected set of connectors.
If the AUTH_HUB_AUTOMATIC
is set to true
then all OAuth requests for all installed connectors that do not have clientId/clientSecret (OAuth 2), or consumerKey/consumerSecret (OAuth 1) configured through the Backoffice (Connector Configuration), will be redirected to the Authentication Hub. This is useful, if you want to use all the available connectors, but don't want to create all the necessary OAuth apps.
If the AUTH_HUB_AUTOMATIC
is set to false
, Appmixer checks the connector configuration. If the connector has clientId/clientSecret (OAuth 2), or consumerKey/consumerSecret (OAuth 1) configured through the Backoffice (Connector Configuration), Appmixer will not use the Authentication Hub (all OAuth requests will go directly from your Appmixer tenant to the relevant OAuth 3rd party). If you add authHubUrl
to the connector configuration, all the authentication requests will be redirected to the Authentication Hub URL provided:
This page contains additional system configuration options that are provided for self-managed Appmixer installations. See the System Configuration for the rest of the configuration options. The below options can be set via the environment variables on the instances of the Appmixer engine.
By default set to info
. It can be changed to error
, warn
or debug
.
When set to false
, the component's input/output messages won't be logged into Elasticsearch.
Important! Appmixer Insights and Designer log messages won't contain any items if logging data messages are turned off.
Configure an HTTP proxy. All HTTP(S) requests from Appmixer will be redirected to the proxy URL.
Most of the connectors in Appmixer require user authentication. That can be represented as OAuth access tokens, API keys, or username/password combinations.
To enable token encryption, set the ENCRYPTION_ENABLED
environment variable to true
. With that, also set the ENCRYPTION_SECRET
environment variable to a secret string (see below for an example on how to generate it).
If you lose the encryption secret, you will not be able to recover the encrypted tokens.
Appmixer contains a MinIO plugin. If this plugin is turned on, Appmixer will store all user files (files created either through Appmixer flows or through the /files API) in the MinIO/S3 server.
The plugin cannot migrate existing files from MongoDB to MinIO. It has to be turned on when Appmixer is installed before the files are created. If you already have files in MongoDB and want to start using MinIO, you have to migrate the data.
To enable the plugin, add minio
to the SYSTEM_PLUGINS (comma-separated list of plugins) ENV variable (this variable cannot be set dynamically through the Backoffice - System Configuration):
If you want to use AWS S3, use the following permissions:
Appmixer provides an API to reset forgotten passwords. This works together with the Appmixer Studio interface (not the Appmixer SDK).
In order to create a link that can be sent to the user, the Appmixer engine needs to know the frontend URL, there are two variables that can be set for that:
APPMIXER_FE_URL
The Frontned URL
http://localhost:8080
RESET_PASSWORD_FE_URL_SUFFIX
URL path with the reset password form
reset-password
Without any changes, the link will be http://localhost:8080/reset-password?code={{code}}.
That link has to be then delivered to the user. There are two ways this can be done:
You can register a system webhook that will be triggered every time a user requests to change their password. The webhook URL can be registered under the key WEBHOOK_USER_FORGOT_PASSWORD and the JSON object sent to that URL will be:
You can use Appmixer to create a simple flow, that would send emails with the reset password link.
The other way is to configure the SMTP server, Appmixer will then send an email with the reset password link to the user's email address.
MAIL_SMTP_HOST
SMTP server address
MAIL_SMTP_PORT
SMTP server port
465
MAIL_SMTP_USER
username
MAIL_SMTP_PASS
password
MAIL_FROM_NAME
Sender name
Appmixer
MAIL_FROM_EMAIL
Sender email
info@appmixer.com
FORGOT_PASSWORD_MAIL_SUBJECT
Reset password email subject.
Reset your password
FORGOT_PASSWORD_MAIL_BODY
The reset password email body.
See below
The default email body:
If the forgot password webhook is configured as explained above, the Appmixer engine will not send the email to the user and it will trigger the webhook instead.
Components produce messages using the context.sendJson()
function. An internal quota mechanism controls how many messages a user can produce.
This is the default configuration:
Each call to context.sendJson()
increases the quota. If a limit is reached, the message is placed in a Slow Queue. Messages in the Slow Queue are processed at a much slower rate and only when sufficient resources are available. This ensures that one user’s flows do not consume excessive resources and block other users.
If you want to change the default values, you can use the Env variables QUOTA_CONTEXT_SEND and QUOTA_CONTEXT_SLOW_QUEUE, you can set them in the Backoffice.
QUOTA_CONTEXT_SEND default value, you can copy&paste this to the Backoffice and modify.
QUOTA_CONTEXT_SLOW_QUEUE default value, you can copy&paste this to the Backoffice and modify.
The garbage collector for files can be turned off with GC_FILES_ENABLED (set to false). The behavior can be controlled by a set of rules GC_FILES_RULES.
This is the default set:
The value of GC_FILES_RULES is a stringified array of rules. There can be a different rule for each scope of users. Each rule has to have exactly 3 properties - scope
, ttl
and hardLimit
.
Example:
In this example, the admin users can store files up to 90GB and their files will be deleted after 1440 hours (60 days).
When the users exceed the hardLimit
, they he will not be able to create more files. To get unblocked, they must delete some of their files and get under the limit.
The garbage collector does not remove files uploaded via the Files API, or files marked as archived.
If you successfully installed the Appmixer self-managed package, you should be able to open the Appmixer front-end application at http://localhost:8080. You should see the sign-in page:
Click on the "Sign up" button to create a new account:
The main system that manages flows, accounts, users, orchestrates components within running flows and provides a HTTP REST API interface to access all the entities within Appmixer. The Appmixer engine is implemented in NodeJS and can run on a single node or in cluster. The engine is horizontally scalable providing high availability and fault tolerance capabilities.
JavaScript HTML 5 SDK that allows to seamlessly embed any of the Appmixer UI widgets (including the drag&drop workflow designer) to any 3rd party web page. The SDK communicates with the engine via REST API. Appmixer JavaScript SDK offers heavy customization capabilities, including changing of the look&feel (via themes), localization of all the text in the UI and even implementing custom component configuration Inspector fields.
A standalone admin panel providing overview of all the flows and users in Appmixer together with system and modules configuration, ACL lists, system webhooks and more.
Command line tool for development, testing and deployment of custom connectors. It can also be used to manage, export and import flows or automatically generate connectors from OpenAPI specification.
Appmixer Engine uses RabbitMQ as a message broker for all data messages passing through the running flows. Also, RabbitMQ serves as an intermediary for logs before entering ElasticSearch.
Storage for all static data such as:
Flows
Users
System Configuration
Accounts
Component States
Dead-letter collection
Component Behaviour (code)
Modifiers (code)
Files (GridFS)
Data Stores
Telemetry
Key-value store that holds the state of the cluster, provides caching and synchronization (distributed locks).
Search & analytics engine that stores all the activity of all the running flows, system logs & errors. Logs can be retrieved by using the Appmixer REST API (Insights). Alternatively, standard tools such as Kibana can be used for in-depth analysis, technical support and maintenance.
Clients (Appmixer Studio, Appmixer JavaScript SDK) provide a drag&drop No-code designer for workflow automations. In design phase, the user is adding components to a flow, configures components, assigns accounts and interconnects components to form a data flow pipeline, possibly with logic and loops.When configuring components, the user can use variables to reference data that will be available at the flow runtime. These variables are outputs of components back in the chain (collection of all the outputs of all the ancestors of the configuring component).Variables can be modified at the flow runtime by applying modifiers. These modifiers are configurable and are represented as parametrized JavaScript functions that manipulate a single input. Modifiers can be chained to produce more complex expressions.The result of the design phase is a new or updated flow descriptor (JSON representation of the flow graph) and metadata, together with account assignment (externally linked) and modifiers (externally linked). Note that the flow descriptor in itself is not complete, the engine assumes the linked components and modifiers exist in the system. Therefore, copying a flow descriptor to another tenant can only work if the linked components and modifiers also exist in the target tenant.
Flow is in the running phase when its configuration has been completed and the flow has been started. From this point, the flow does not require any user interaction and is executed and maintained by the engine in the background. During the flow running phase, the engine is responsible for passing external inputs to the associated trigger components (e.g. webhooks), scheduling polling type of triggers, orchestrating message passing between connected components, handling errors, retries, making sure defined quota limits are honored and logging all activity. The engine reports all unexpected events via system defined webhooks. This allows the customer to define custom actions in case e.g. the flow can’t run since a component in it lost authentication tokens (e.g. a 3rd party connected account was externally revoked). The engine has a built-in support for the most common authentication mechanisms (OAuth 1, OAuth 2, API keys, Basic auth, …) and can be extended with new ones. It automatically handles token refreshing for relevant authentication types. Components within a flow (and in general, when defined) don’t know about each other. They are black boxes that can be arbitrarily interconnected to produce different automations.If a message cannot be processed (e.g. a component tried to call an API which keeps failing multiple times - configurable) the engine stores such a message in a dead-letter collection in MongoDB and notifies a system configured webhook. This allows the customer to further inspect such messages, retry their processing and/or notify the end-user of such errors. The engine provides built-in scheduling mechanism, offering component developers an intuitive way for defining different types of timers, schedulers and throttles.
Appmixer command-line tool.
Use the Appmixer CLI tool to interact with the Appmixer engine remotely from the command line. It can be used to list flows, start/stop flows but more importantly, to develop and publish custom components.
Download and install NodeJS: (version 18 is required)
npm install -g appmixer
Display the command options with the -h
option:
Each command has its own help information:
Login to your Appmixer account and enter your password:
The best way to start implementing your own custom components is to use the generator tool to generate a sample component. This gives you the basic skeleton of your component/service that you can later tweak. Use the appmixer init example
command to generate a sample component:
As you can see, we have to give this command a type
of example we want to generate. We will start with the most simple service type that does not use any authentication (OAuth1, OAuth2, API keys). In other words, the component will not ask the user to connect any account in the Inspector panel when using the component in a flow.
The command prints all the generated files and the directory structure. Note that we have just generated a working component with the myservice.mymodule.MyComponent
type.
Now we're ready to pack our service (i.e. create a zip archive with all the generated files) using the appmixer pack appmixer/myservice
command:
You can pack the entire service or just the module or even individual components by providing the path to the appmixer pack
command.
The pack
command generated the appmixer.myservice.zip
file in the current directory.
The last step is to publish our component for our users to use. This is done using the appmixer publish
command:
Our component is now published and ready to be used:
Notice the labels and icons of the service in the component panel on the left and of the component in the Inspector selector match our definitions from the myservice/service.json
and myservice/mymodule/MyComponent/component.json
manifest files.
To see all the available components uploaded to Appmixer, use the appmixer component ls
command:
If you decide your component is no longer needed, you can remove your component from the system with the appmixer remove
command:
The remove command lets you specify any portion of the fully qualified component type. For example, if you want to remove the entire service, you can do that with:
It's important to note that removing a component that is used in any of the running flows will cause the flows to stop working. The flows will start generating errors that will be visible in the Insights section. The remove
command does not automatically stop the flows. Always make sure the component is not used in any of the running flows before you remove it.
IMPORTANT: Removing a component cannot be undone!
You can re-publish (appmixer publish
) your component which effectively replaces the old component with the new one.
Note that if you "dramatically" change your component, e.g. removing ports, flows using the component type might require re-configuration since the ports that were used to connect to other components will not exist. The same goes for other major changes: changing output parameters that are used in connected components, changing inspector config and properties, etc.
If want to run the next commands on Windows, make sure you escape the JSONs correctly. For example, instead of
appmixer test component HelloAppmixer -i '{"in": {"text": "abc"}}'
you have to use
appmixer test component HelloAppmixer -i "{\"in\": {\"text\": \"abc\"}}"
Writing/updating code of your component, re-publishing it and re-configuring/restarting your flow every time you need to test your component would be a long process. Therefore, the Appmixer CLI tool provides a command to test your component locally on your machine, before publishing it to Appmixer. The appmixer test
command allows you to test your component by sending messages to it, configuring properties and testing authentication methods:
We'll start by exploring the component testing tool:
Let's say we want to send a message to our component and see how it reacts, i.e. what is the output of our component. We know our component has an input port called in
and requires the sourceData
property as part of the incoming messages (see the component.json
file of your MyComponent, especially the inPorts
section). We can use the test
command for this:
The command prints out a lot of useful information, for example, the output of the component (see "Component sent a message to its output port: out"). Since our component just forwards the same data that it received, we see the same object we sent to it: { sourceData: 'foo' }.
Now suppose we have a bug in our code in MyComponent.js, a syntax error:
Now re-running the test gives us:
When your component is published, you can always download it back to your local file system. To download the source code of your component, use the appmixer download
command:
In our case, the download command would look like:
You can list all your flows using the appmixer flow ls
command:
If you want to see just one flow and its stage (running/stopped), you can pass the ID of the flow in the flow ls
command:
To see the flow descriptor of a flow (i.e. JSON object that represents the entire configuration of the components in the flow and their connections), add the --descriptor
or -d
flag:
You can start and stop flows using the appmixer flow start
and appmixer flow stop
commands:
To remove flows, use the appmixer flow remove
command:
Note that the removed flow will be automatically stopped if it was running. Also, note that this action cannot be undone.
You need admin privileges to run these commands. You should not modify Modifiers if there are any running flows using them. Such an operation would break the flows.
The next command will download modifiers from Appmixer and save them into the modifiers.json file.
The next command will publish modifiers into Appmixer.
The next command will delete existing modifiers from Appmixer. The next time Appmixer starts, it will load the default set of Modifiers.
Appmixer supports all cloud deployment models:
Public cloud (AWS, GCP, Azure, ...)
Private cloud
VPC
Hybrid cloud
All 3rd party managed services and integrations are optional and customizable. For example, you can choose for which of the supporting technologies you prefer to use a 3rd party managed alternative (e.g. local MongoDB vs MongoDB Atlas, AWS DocumentDB, ...). You can also choose what integrations you would like to offer to your end/internal-users and configure your own notifications (e.g. email) via customizable system webhooks.
Appmixer uses multiple supporting technologies to function: MongoDB, RabbitMQ, Redis and ElasticSearch. All the supporting technologies are open-source.
Supporting technologies can be either installed and managed on an “as-is” basis or 3rd party managed (e.g. CloudAMQP’s RabbitMQ as a Service can be used).
For help with deploying Appmixer with full compatibility with AWS managed services for all the supporting technologies (Amazon MQ, ElasticCache, DocumentDB, OpenSearch), please contact our customer support (support@appmixer.com).
Appmixer self-managed package includes Docker and Kubernetes configuration files for easy deployment.
Appmixer engine (together with Appmixer Backoffice and Appmixer Studio) images are available via Appmixer Docker Registry.
This tutorial shows you how to create Appmixer virtual users that shadow your own user accounts in your own user management system. Moreover, we also show how to group users into teams and how to limit the set of components each team has access to.
When you embed Appmixer functionality/UIs into your product, you need to create Appmixer virtual users. These user accounts are only internal, your end-users don't need to sign-up anywhere and won't even notice. The purpose of virtual user accounts in Appmixer is to associate Appmixer entities (flows, data stores, files, connected 3rd party accounts, logs) with your own user accounts.
Usually, you create Appmixer virtual user accounts automatically and on the fly, at the time your end-user requests to see a page where an Appmixer functionality is embedded. Therefore, not all your users need a shadow Appmixer virtual user account but only those that will interact with the embedded Appmixer widgets (Integrations, Workflow Designer, ...).
The first thing you need to do is to provide an HTML container element inside which you want to render the FlowManager:
Note that you also need to include the Appmixer SDK on your page:
And then initialize the SDK by passing your Appmixer base URL (the Appmixer Engine location):
At this point, you're ready to authenticate your user to Appmixer in the background or create a new virtual Appmixer user if your user account has not yet been associated with one. Let's assume that your client-side code has an information about your user profile stored in profile
object that might look something like this:
If this is the first time your user is about to interact with Appmixer in your app, then your user profile does not have any Appmixer virtual user credentials stored. Therefore, you should generate a secret that we'll use as the user's password to Appmixer and create a new Appmixer virtual user. If your user profile already contains Appmixer virtual user credentials, you can simply authenticate the user to Appmixer and get a token back that you can use to initialize the SDK:
This is it. Now your web app creates Appmixer virtual users as needed and your users can start interacting with Appmixer widgets.
One disadvantage of the client-side approach is that (for security reasons), you cannot change the user scope (a string that can be used to group users into teams in Appmixer and therefore set common rules e.g. for accessing different set of components for different teams). If you don't require this functionality, you're fine with the client-side approach. If you do, you need to create your virtual users from your backend application using the Appmixer REST API. The next section shows you how you can do that.
The response contains the user object together with their token:
To create a new virtual user, initiate a POST request to the /user endpoint:
The response contains the newly created user token:
Use the token to request the user profile:
The response contains the user object together with the user ID:
You can also manually validate that a user has been created in the Appmixer Backoffice interface:
As you can see, all newly created users have the default user
scope. This scope can be replaced by any other string (e.g. ID or slug of a team that you want to group users under). Having a custom scope grouping multiple users allows you to define rules that e.g. limit the particular scope to only a selected set of modules/components (see below). To change the user scope, call the following endpoint passing a list of scopes that you want the user to have. Note that you need to use the admin user token to change a user's scope (for security reasons so that regular users can't change their own scope). You also need to use the user ID that we retrieved from the user profile endpoint in the previous step:
Again, you can validate in the Appmixer Backoffice that the user's scope has indeed changed:
Or you can search users by their username or part of it using the pattern
query parameter:
By default, Appmixer ships with a set of ACL rules that apply for the admin
and user
scopes. If you don't need to create new scopes to group users, you're usually fine with the default setting. However, since we create a brand new scope acme1
, users with this scope won't be able to read and create flows and won't have access to any modules/components.
ACL rules in Appmixer are divided into two categories: routes and components. You can see the two categories when you visit the ACL page of your Appmixer Backoffice interface:
The routes
category defines ACL rules that apply to the routes of the Appmixer Engine API (the endpoints). By default, all users with the admin
and user
scope have access to all the endpoints:
In order for users with our new scope acme1
to be able to access all the endpoints as regular users can, we need to add a new rule. This can be done either via the Backoffice interface ("Add" button):
Or programatically via the Appmixer REST API. A common practice is to first get the existing set of ACL rules for the routes
category and if the newly created scope is not in the list, just add it (note again that you need admin user token to manipulate ACL rules):
The response looks something like this:
Since our acme1
scope is not in this list (look for the role
attribute), we can add a new rule that allows users with this scope to access their flows:
Where our routes-acl.json
file looks like this (the Appmixer API currently allows replacing all the rules at once, therefore, for convenience, we stored our rules in a file to make our curl
command a little bit more readable):
Note that the user can technically access the Appmixer Studio but since our goal is to embed Appmixer to your own product, your virtual users won't be visiting the Appmixer Studio at all. Instead, they'll be interacting with the embedded Appmixer widgets only. The purpose of the above test is to quickly validate (especially the first time you're creating virtual users programatically) what the user has access to.
Also notice that if you try to create a new flow on behalf of the user by clicking on the "Create flow" button, the list of available modules will be empty:
This is because we haven't yet granted users with the acme1
scope access to any modules/components. To add ACL rules for components, we can follow a similar approach as we did for the ACL routes
category but this time we'll do that for the components
category. Therefore, we can again do it manually in the Appmixer Backoffice by adding a new rule to the components
category. Let's say we'd like to give the user access only to the modules in the appmixer.utils
and appmixer.twilio
namespaces. We can add two new rules in the Backoffice:
Note the wildcard (*
) character after the module namespace. If we didn't do that, although the user will have access to the module itself, they won't be able to see any components within that module (e.g. appmixer.twilio.sms.SendSMS
). The wildcard character makes sure all the components within the appmixer.twilio
namespace are matched. If you don't use the wildcard, you can give users access to only a particular set of components within modules instead of the entire module.
Once you save those two rules and create a flow on behalf of the user in the Appmixer studio, you should see the utility and twilio modules displayed:
To add the components rules programatically via the Appmixer REST API, again, first request the current set of modules:
where the response looks something like this:
And add a new rule to the list so that users in our acme1
scope can access utilities and twilio modules:
Replace the components ACL rules with the above list:
Once you Publish an integration template and visit the Sharing dialog:
You'll notice your integration template has been shared with users under the user
scope by default:
To share the Integration template with your custom scope, just change "user" to your own scope instead and click on the Update button:
Now users in the acme1
custom scope will be able to use your integration template:
In this tutorial, we learned how to create virtual users both using the simple method via the client-side Appmixer JavaScript SDK and also using the more advanced and flexible method via the Appmixer REST API. We learned how to group users, set different access rights for these groups of users, especially access to different set of components and integration templates. You should now be ready to embed Appmixer into your own product and create shadow virtual users for your own user accounts.
First set the Appmixer API URL. This is the URL of your hosted Appmixer engine instance or your own custom URL where the self-managed engine is located. If you have a trial package or local installation, you can use .
Before you publish a component. Your user account has to have property vendor
set to a string or an array of strings. The value depends on what vendor
is used in component(s) you're about to publish. More about that in . In the examples in this section we use appmixer
as a vendor
, but you should use your own. If your company is called acme
then the vendor
property should be set to acme
as well. You can use Backoffice for that as shown in the next picture.
The easiest way to create virtual user accounts is via the client-side Appmixer JavaScript SDK. The SDK provides methods to authenticate virtual users and to create new virtual users. You can find a full example in the . Let's say you have a page in your web application where you want to display the Appmixer FlowManager UI widget so that your end-users can see the flows they created and create new ones:
To create new or update existing virtual users using the REST API, use the . Since some of the User endpoints cannot be called with a regular user Authentication Bearer token, the first step is to authenticate using any of your admin users and use the admin user token instead:
Note that since we created a brand new scope and it's the only one that our new user has, we also need to configure ACL rules for this user so that they are able to create and read flows and access some modules in order to use them in their flows. See the next section on how to set this up. But before we jump into ACL rules, let's just quickly review how you can search for users using the REST API. To find all users with a certain scope, you can call the endpoint and filter by scope:
At this point, you can even try to sign-in on behalf of this user to the Appmixer Studio interface () to validate that the user is indeed created and that they can use Appmixer:
Now that we have created a new virtual user, defined and configured a custom scope, we can take advantage of this new scope not only to limit the set of available components for users in this scope but we can also share with only users under a certain scope. By default, integration templates are shared with the user
scope. This happens when you publish a new integration template in the Wizard builder:
By default, anyone can create a new user. This enables the sign-up feature. The API endpoint can be secured. You may want to restrict access to this endpoint and control how the virtual users are created. To do so, set the API_USER_CREATE_SCOPE to admin
.