Menu
Articles in this section
Building AppConnect Recipes
A Recipe is a trigger plus a sequence of steps. This page covers what fires a recipe, what the steps can do, how conditions are evaluated, and how to make the result survive contact with real data.
On this page:
Triggers
A trigger determines which event causes the recipe’s actions to run. Events fire in connected apps when something happens, such as a new Contact being created or an existing Ticket being updated, when a new line is added to a file, or on a schedule.
Adding a new file attachment in Insightly can also act as a trigger. When configuring a trigger for creation of a new entity, select the object name File Attachment.
Depending on the app’s API, AppConnect either receives events in real time or polls for them.
Trigger behaviour
Recipes pick up trigger events and queue them in sequence, processing them as jobs. The recipe maintains a cursor and moves through the queue synchronously.
- In-sequence delivery. Events are processed in chronological order, oldest first, or in the order AppConnect received them.
- Durable cursor position. A recipe remembers what it has processed across stopped and running states. Restarting picks up where it stopped and processes everything since.
- No duplication. Each recipe keeps a record of the events it has seen and will not process the same one twice.
- Flow control. Events are processed synchronously, one job finishing before the next starts, with concurrency adjustable to raise throughput.
- Guaranteed delivery. For polling triggers, AppConnect guarantees delivery. Temporary server downtime or an unstable network does not lose events.
⚠️ Webhook events can be lost. Webhooks power most real-time triggers and are inherently lossy. Most AppConnect-built real-time triggers carry a backup polling mechanism to catch missed events, but the HTTP webhook trigger is a notable exception and has no such backstop.
Trigger mechanisms
Polling triggers query the app at intervals. Frequency depends on your AppConnect plan and can be as low as every 5 minutes. A single poll can yield several events and therefore create several jobs.
On first start, a polling trigger fetches everything after the From date. Afterwards it fetches at the plan’s interval. Stopping the recipe stops the fetching; restarting fetches everything since it stopped. A recipe stopped on 1 February and restarted on 10 March fetches everything created in between.
Real-time triggers are built on asynchronous notification, usually webhooks, and typically require registration in the connected app so it knows to notify AppConnect. There is no polling delay and no wasted checks.
Real-time triggers other than HTTP ones are generally webhooks backed by slower polling, on the order of hourly rather than every 5 minutes. That polling backstop is also what makes a From date selectable at recipe start.
Scheduled triggers run at specified days and times, hourly, daily, monthly. Two variants exist. New scheduled event sets a first trigger time and an interval. New scheduled event (advanced) sets days of the week and times; entering only minutes, say 30, triggers 24 times a day at half past each hour, while filling both hour and minute triggers once a day.
⚠️ Scheduled triggers re-return events already processed. Unlike other trigger types they return events that have been picked up before, so recipes on a scheduled trigger need their own guard against reprocessing.
Scheduled triggers return events in batches with a user-specified maximum size. With a batch size of 100 and 420 new events, five jobs are created: four of 100 and one of 20.
Workato Scheduler Trigger Setup Configuration
Trigger dispatches
Single triggers return one event at a time and suit continuous real-time synchronization, such as moving closed Opportunities into an ERP as they close. Most AppConnect triggers are single triggers.
Batch triggers return lists rather than single events and suit throughput, such as moving high-volume activity data into a warehouse. Batch size is set in the trigger configuration.
Trigger Events Grouped Into Batches of 1000
Since and From
The Since or From setting makes a recipe fetch past events from a specified date and time rather than only events created after it started.
Not every trigger has this parameter. Where it is absent, the start point is predetermined, usually as an offset from recipe start: at first start, an hour before, or a day before. The trigger hint for the connector says which.
⚠️ The Since/From value can only be set once. It locks after the recipe has been started for the first time and cannot be changed afterwards.
Trigger conditions
Trigger conditions filter which fetched events get processed, such as processing only Insightly Contacts from California.
- Create a new recipe.
- Create a new trigger and select an object.
- Enable the Set Trigger condition toggle.
- Add a field to Trigger data field.
Alternatively, check the Trigger IF checkbox to reveal the trigger datatree and the variables available for the condition. Add further conditions with +, choosing OR or AND; from the third condition onwards, the operator already selected applies to all of them.
⚠️ Conditions are applied after events are fetched, not during. AppConnect retrieves all new Contacts created in the last five minutes, then filters out those not from California. With a New Insightly Contact trigger, a Contact later updated to be in California is never picked up, because the update is not a creation event. If a recipe seems to be missing events you expected, this is the usual cause.
⚠️ Conditions check state, not change. A condition does not monitor a field changing. Setting a condition to sync only closed-won Opportunities means every subsequent update to an Opportunity already in closed-won is picked up again, rather than only the moment it first becomes closed-won.
Values are case sensitive and must be exact.
Setting Up Trigger Conditions in Workato Recipe
Actions
Every connector exposes a set of actions, and AppConnect adds utility actions of its own such as creating a pie chart or fetching a file from a URL. Actions group into five types.
Create entity. Creates a standard or custom object. Creating an Insightly organization requires at least an Organization Name. Typically returns an ID for looking the new object up; sometimes the whole object, depending on the app’s API.
Update entity. Changes an existing object. Input is usually data that uniquely identifies the target.
Search entities. Returns all objects matching the criteria, as a list.
⚠️ Search matches on “contains”, not equality. Searching for a Contact with the email insightly@gmail.com also returns myinsightly@gmail.com.
⚠️ Search returns an empty list rather than an error when nothing matches. No error is thrown, so the recipe continues and any downstream action depending on the search result fails instead. Guard the result with a conditional step rather than relying on the search to stop the job.
Get list of entities. More precise than search: it takes a unique ID and returns that object’s details. Unlike search, get throws an error when the object is not found.
Delete entity. Takes an ID. Most apps do not support delete, so AppConnect’s coverage is narrow.
⚠️ Delete permanently loses data. Understand the implications before putting a delete action into a recipe.
|
1 Creating a New Organization Entity in Insightly |
2 Insightly Get Single Entity Setup Configuration |
Custom actions
A custom action makes an HTTPS request to an Insightly API endpoint using the credentials from the existing Insightly AppConnect connection, which avoids creating a separate connection for endpoints the connector does not already cover.
- Find the Insightly API endpoint in the API documentation.
- Name the custom action and set it up manually or through the guided setup.
- Pick the HTTP method from the Method dropdown, according to the endpoint.
- Enter the endpoint in the Path textbox. Custom actions use the Base URI from the connection, so the full address is not needed. Include any required parameters and the request body.
- The API response returns in JSON.
Steps
Steps are either actions or control flow statements.
Action step. Carries out an operation in the target app, typically create, update or search. Each takes input fields and usually returns an output datatree whose fields can be mapped in later steps.
Conditional action step. Actions indented inside the block run only when the condition is true. The canonical pattern is a pair: if the organization was found, update it; if it was not, create it.
Repeat step. Runs the indented actions once for every item in a list. The input is a list, and actions inside the block should use data from the step’s Foreach output datatree, which is what makes each iteration operate on the right item. List-type pills are identifiable by their stack icon.
Repeat in Batches. For when an upstream system produces data faster than a downstream system can absorb it. Set Repeat Mode to Batch of items rather than One item at a time, then set Batch size.
ℹ️ Batch size defaults to 100 when left unspecified or set below 1. A data pill can be used as the batch size.
Call Recipe step. Runs another recipe, the callable recipe, in the manner of a function call. It is the mechanism for reusing recipe logic.
⚠️ Callable recipes run synchronously. The calling recipe blocks until the called recipe finishes.
Stop step. Ends a single job. Configurable to mark the job failed or successful, which matters because marking it failed is what brings it to the recipe owner’s attention for a rerun.
Workato Recipe Repeat Step Configuration Panel
Stop Job With Error Configuration Panel
Error handling
Action with error handler
The error handler step is AppConnect’s try/catch. It has two blocks: Monitor holds the actions being watched, and Error holds what happens if any of them fails. If everything in Monitor succeeds, the Error block is ignored.
The Error block is where you retry, notify, or roll back by deleting records the failed job created or half-created.
Auto-retry
Monitor block actions are not retried by default. Retry up to three times, choosing the interval between attempts, and optionally set a condition that must be met before a retry happens.
The condition is what makes retry useful rather than wasteful. Retrying on a timeout or transient network failure is worth doing; retrying on a 401 is not, because a permissions problem will fail identically every time. The source’s example retries only when the error message does not contain 401.
Best practices
- Error monitoring steps. Add through Add New Step > Handle Errors. Everything to be watched must be nested under the Monitor step. After the configured retries are exhausted, anything nested under On Error Retry runs.
- Conditional actions. Validate input before important actions, through Add New Step > Conditional Action.
- Stop actions. End or fail a recipe early when validation finds a problem, which saves processing time and API calls. The stop reason can be customized, and that text appears in the job report and in error emails.
- Custom error emails. Attach errors as a file with a descriptive name such as errors.txt, and include data pills for the job URL, the recipe URL, and when the recipe ran. Those pills are in the Properties tab of the data tree.
Concurrency handling
Two recipes updating the same record at the same time can silently overwrite each other. Concurrency handling checks that an update is based on the current version of a record before applying it.
Insightly assigns every record a version tag, an ETag, returned in the payload and updated each time the record changes. If-Match is how a recipe says “apply this only if the record is still the version I read”.
| Outcome | What happened |
|---|---|
| Update applied | The If-Match value matched the record’s current ETag. The change was made against the current version. |
| Update rejected, 412 Precondition Failed | The If-Match value did not match. The update was based on a stale version. The recipe decides whether to fail or retry. |
Setup
Concurrency handling applies only to Update existing entity actions.
In the Update existing entity action, use the optional Headers field, which takes a key-value pair. Key: If-Match. Value: the ETag field from the previous step, added as a data pill.
⚠️ Missing headers mean no protection, silently. If the Headers field or the If-Match key-value pair is absent, concurrency handling is not applied. There is no warning; the update proceeds exactly as it would have before.
A recipe that handles the 412
The source’s pattern reloads the record and retries up to three times:
- Create a new recipe, then a trigger, and select an object.
- Add a Monitor step using the Handle errors action.
- Put the Update existing entity action inside the Monitor block.
- Set up an Error Found? block to catch the 412.
- Set Retry actions in Monitor block? to Do not retry.
- Add an IF condition action: Data field Error message, Condition contains, Value 412.
- Optionally add a wait before retrying, using Scheduler by Workato > Wait for time duration, with Interval set to a custom value in seconds.
- In the Yes block of the IF condition, add another Monitor step using Handle errors.
- Add Get single entity by object name and ID, selecting the object and setting Entity Id to the Record Id from the earlier step.
- Copy the Update existing entity action carrying the concurrency headers and paste it after the Get. Remap the ETag in the Headers value to the one from the Get action, and remap any other fields as needed.
- Configure the Error found block: number of retries, wait duration, and Data Error message, Condition contains, Value 412.
If the 412 persists or new errors appear, the recipe can either continue or stop the job.
IF conditions
IF conditions appear in trigger conditions, in conditional action steps, and in the auto-retry condition on the error monitor step.
Each condition has three parts: data on the left, usually a variable from your app such as case status; the condition; and the value on the right, usually a static value to check against. Conditions combine with AND or OR.
⚠️ Data and values are case sensitive throughout.
⚠️ A condition used against the wrong data type can break the recipe rather than just fail. An invalid condition may stop the recipe starting at all. On a trigger it can throw a trigger error after the recipe has started, leaving it unable to pick up any events, or silently filter out every event.
There are 14 conditions.
Contains
This condition checks if the data contains the value. It is case-sensitive - make sure to downcase or upcase both before comparison if you are not concerned about case sensitivity. It works with any characters, numbers, words, letters, and symbols.
This condition is only valid for array and string data types.
| Trigger data | Condition/value | Picked up by recipe? |
|---|---|---|
| "UI bug" | contains "bug" | Yes |
| "UI BUG" | contains "bug" | No |
| "Instructions unclear" | contains "bug" | No |
| "" | contains "bug" | No |
| nil | contains "bug" | No |
| 12345 | contains 123 | No |
| [1, 2, 3] | contains 1 | Yes |
| [1, 2, 3] | contains [1, 3] | No |
| ["abc", "pqr", "xyz"] | contains "abc" | Yes |
| ["abc", "pqr", "xyz"] | contains ["abc", "pqr"] | No |
Starts with
This condition checks if the trigger data string begins with the value. It is case-sensitive - make sure to downcase or upcase both before comparison if you are not concerned about case sensitivity. The Starts with condition searches only for exact matches, and null values will not be picked up.
This condition is only valid for string data types.
| Trigger data | Condition/value | Picked up by recipe? |
|---|---|---|
| "(408) 555-6928" | starts with "(408)" | Yes |
| "408 555-6928" | starts with "(408)" | No |
| "(650) 555-2395" | starts with "(408)" | No |
| "" | starts with "(408)" | No |
| nil | starts with "(408)" | No |
| 12345 | starts with 123 | Trigger error thrown |
| numeric_type_pill | starts with 123 | Trigger error thrown |
| numeric_type_pill | starts with "123" | Yes #if pill = 12345 |
Comparing non-string data types with a starts with condition throws a trigger error. For example, comparing a number type with a number type will throw an error. However, if the trigger data input field is a non-string datapill, and the value is a string, AppConnect converts the datapill’s value into a string value for you and does the comparison, evaluating to true if the converted value meets the condition.
Ends with
This condition checks if the trigger data ends with the value. It is case-sensitive - make sure to downcase or upcase both before comparison if you are not concerned about case sensitivity. It works with any characters, numbers, words, letters, and symbols. If the field you specify in your condition is left blank in the application you’re using, no event will be picked up.
This condition is only valid for string data types.
| Trigger data | Condition/value | Picked up by recipe? |
|---|---|---|
| "(408) 555-6928" | ends with "6928" | Yes |
| "408 555-6928" | ends with "(6928)" | No |
| "(650) 555-2395" | ends with "6928" | No |
| "" | ends with "6928" | No |
| nil | ends with "6928" | No |
| 12345 | ends with 345 | Trigger error thrown |
| numeric_type_pill | ends with 345 | Trigger error thrown |
| numeric_type_pill | ends with "345" | Yes #if pill = 12345 |
| numeric_type_pill | ends with "345" | No #if pill = 123 |
Comparing non-string data types with an ends with condition throws a trigger error. For example, comparing a number type with a number type will throw an error. However, if the trigger data input field is a non-string datapill, and the value is a string, AppConnect converts the datapill’s value into a string value for you and does the comparison, evaluating to true if the converted value meets the condition.
Does not contain
This condition is the opposite of the contains condition. It checks if the trigger data DOES NOT contain the value. It is case-sensitive - make sure to downcase or uppercase both before comparison if you are concerned about case sensitivity. If the field you specify is left blank in the application you are using, the Doesn’t contain condition will not count it, and no event will be picked up. This can be circumvented by using the Is true or Is not true conditions with a string formula, as shown in the Is true section below, or by pairing it with the Is present condition.
This condition is only valid for array and string data types.
| Trigger data | Condition/value | Picked up by recipe? |
|---|---|---|
| "UI bug" | doesn't contain "bug" | No |
| "UI BUG" | doesn't contain "bug" | Yes |
| "Instructions unclear" | doesn't contain "bug" | Yes |
| "" | doesn't contain "bug" | Yes |
| nil | doesn't contain "bug" | No |
| 12345 | doesn't contain 123 | No |
| [1, 2, 3] | doesn't contain 1 | No |
| [1, 2, 3] | doesn't contain [1, 3] | Yes |
| ["abc", "pqr", "xyz"] | doesn't contain "abc" | No |
| ["abc", "pqr", "xyz"] | doesn't contain ["abc", "pqr"] | Yes |
Does not start with
This condition is the opposite of the starts with condition. It checks if the trigger data string DOES NOT begin with the value. It is case-sensitive - make sure to downcase or uppercase both before comparison if you are concerned about case sensitivity. If the field you specify is left blank in the application you are using, the Doesn’t start with condition will not count it, and no event will be picked up. As with the Doesn’t contain trigger condition, this can be circumvented by using a string formula with the Is true formula as shown in the Is true section below, or by pairing it with the Is present condition.
This condition is only valid for string data types.
| Trigger data | Condition/value | Picked up by recipe? |
|---|---|---|
| "(408) 555-6928" | doesn't start with "(408)" or "(669)" | No |
| "408 555-6928" | doesn't start with "(408)" or "(669)" | Yes |
| "(650) 555-2395" | doesn't start with "(408)" or "(669)" | Yes |
| "" | doesn't start with "(408)" or "(669)" | Yes |
| nil | doesn't start with "(408)" or "(669)" | No |
| 12345 | doesn't start with 123 | Trigger error thrown |
| numeric_type_pill | doesn't start with 123 | Trigger error thrown |
| numeric_type_pill | doesn't start with "123" | No #if pill = 12345 |
| numeric_type_pill | doesn't start with "123" | Yes #if pill = 345 |
Non-string data types Comparing non-string data types with a doesn’t start with condition throws a trigger error. For example, comparing a number type with a number type will throw an error.
Does not end with
This condition is the opposite of the ends with condition. It checks if the trigger data DOES NOT end with the value. It is case-sensitive - make sure to downcase or upcase both before comparison if you are not concerned about case sensitivity. It works with any characters, numbers, words, letters, and symbols. If the field you specify is left blank in the application you are using, the Doesn’t end with condition will not count it, and no event will be picked up. Similar to the Doesn’t contain trigger condition, this can be circumvented by using a string formula with the Is true formula as shown in the Is true section below, or by pairing it with the Is present condition.
This condition is only valid for string data types.
| Trigger data | Condition/value | Picked up by recipe? |
|---|---|---|
| "(408) 555-6928" | doesn't ends with "6928" | No |
| "408 555-6928" | doesn't ends with "(6928)" | Yes |
| "(650) 555-2395" | doesn't ends with "6928" | Yes |
| "" | doesn't ends with "6928" | Yes |
| nil | doesn't ends with "6928" | No |
| 12345 | doesn't ends with 345 | Trigger error thrown |
| numeric_type_pill | doesn't ends with 345 | Trigger error thrown |
| numeric_type_pill | doesn't ends with "345" | No #if pill =12345 |
| numeric_type_pill | doesn't ends with "345" | Yes #if pill =123 |
Non-string data types Comparing non-string data types with a doesn’t end with condition throws a trigger error. For example, comparing a number type with a number type will throw an error. However, if the trigger data input field is a non-string datapill, and the value is a string, AppConnect converts the datapill’s value into a string value for you and does the comparison, evaluating to true if the converted value meets the condition.
Equals
This condition checks if the trigger data equals the value. It is case-sensitive - make sure to downcase or upcase both before comparison if you are not concerned about case sensitivity. It works with any characters, numbers, words, letters, and symbols.
This condition is valid for all data types, e.g. booleans, string, integers and floats, dates, arrays.
| Trigger data | Condition/value | Picked up by recipe? |
|---|---|---|
| "Closed" | equals "Closed" | Yes |
| "Closed" | equals "closed" | No |
| "" | equals "Closed" | No |
| "" | equals null | No |
| 'null' | equals nil | Yes |
| nil | equals "Closed" | No |
| 12345 | equals 12345 | Yes |
| 12345 | equals "12345" | Yes |
| 6 - 1 | equals 5 | Yes |
| "Closed".present? | equals true | Yes |
| "Closed".present? | equals "true" | No |
| "Closed".present? | equals 1 | No |
When non-string trigger data is compared to a string value, AppConnect converts the trigger data to a string for the comparison, e.g. 12345 equals “12345” will evaluate to true.
Does not equal
This condition is the opposite of the equal condition. It checks if the trigger data DOES NOT equal the value. It is case-sensitive - make sure to downcase or uppercase both before comparison if you are concerned about case sensitivity. It works with any characters, numbers, words, letters, and symbols.
This condition is valid for all data types, e.g. booleans, string, integers and floats, dates, arrays.
| Trigger data | Condition/value | Picked up by recipe? |
|---|---|---|
| "Closed" | does not equal "Closed" | No |
| "Closed" | does not equal "closed" | Yes |
| "" | does not equal "Closed" | Yes |
| "" | does not equal null | Yes |
| 'null' | does not equal nil | No |
| nil | does not equal "Closed" | Yes |
| 12345 | does not equal 12345 | No |
| 12345 | does not equal "12345" | No |
| 6 - 1 | does not equal 5 | No |
| "Closed".present? | does not equal true | No |
| "Closed".present? | does not equal "true" | Yes |
| "Closed".present? | does not equal 1 | Yes |
Greater than
This condition checks if the trigger data is greater than the value. If value is set to a number, and the trigger data field has a null value, the recipe will raise a trigger error, as computationally, a number cannot be compared with a null value. To resolve this issue, add an is present condition along with the greater than condition.
This condition is valid for string, date, timestamp, integer and number data types.
| Trigger data | Condition/value | Picked up by recipe? |
|---|---|---|
| "2017-06-31T12:00:00.252805-07:00" | greater than "2017-12-31T12:00:00.252805-07:00" | No |
| "2017-06-30T12:00:00.252805-07:00" | greater than "2017-01-31T12:00:00.252805-07:00" | Yes |
| "2017-06-31" | greater than "2017-12-31" | No |
| "2017-06-31" | greater than "2017-01-31" | Yes |
| 5 | greater than 10 | No |
| 5 | greater than 1 | Yes |
| 1.5 | greater than 10.5 | No |
| 1.5 | greater than 1.23 | Yes |
| "abc" | greater than "abcde" | No #ASCII value comparison |
| "abc" | greater than "a" | Yes #ASCII value comparison |
| nil | greater than "2017-01-31T22:00:00.252805-07:00" | Trigger error thrown |
| "2017-06-31" | greater than nil | Trigger error thrown |
| nil | greater than 10 | Trigger error thrown |
| 1.5 | greater than nil | Trigger error thrown |
| "abc" | greater than nil | Trigger error thrown |
Less than
This condition checks if the trigger data is less than the value. If value is set to a number, and the trigger data field has a null value, the Recipe will raise a trigger error, as computationally, a number cannot be compared with a null value. To resolve this issue, add an is present condition along with the less than condition.
This condition is valid for string, date, timestamp, integer and number data types.
| Trigger data | Condition/value | Picked up by recipe? |
|---|---|---|
| "2017-06-31T12:00:00.252805-07:00" | less than "2017-12-31T12:00:00.252805-07:00" | Yes |
| "2017-06-30T12:00:00.252805-07:00" | less than "2017-01-31T12:00:00.252805-07:00" | No |
| "2017-06-31" | less than "2017-12-31" | Yes |
| "2017-06-31" | less than "2017-01-31" | No |
| 5 | less than 10 | Yes |
| 5 | less than 1 | No |
| 1.5 | less than 10.5 | Yes |
| 1.5 | less than 1.23 | No |
| "abc" | less than "abcde" | Yes #ASCII value comparison |
| "abc" | less than "a" | No #ASCII value comparison |
| nil | less than "2017-01-31T22:00:00.252805-07:00" | Trigger error thrown |
| "2017-06-31" | less than nil | Trigger error thrown |
| nil | less than 10 | Trigger error thrown |
| 1.5 | less than nil | Trigger error thrown |
| "abc" | less than nil | Trigger error thrown |
Is true
This condition checks that the trigger data is true. It can also be used to check that the formula provided in the trigger data input field evaluates to true. For example, you can convert string type datapills via string formulas into conditions that evaluates to a boolean.
This condition is only valid for boolean data types. Use it to check a boolean datapill, or a formula that evaluates to true or false.
| Trigger data | Condition/value | Picked up by recipe? |
|---|---|---|
| pill.present? | is true | No #if pill has a nil or null value or is an empty string "" |
| pill.present? | is true | Yes #if pill has a value |
| "Advanced Solutions".include?("Solutions") | is true | Yes |
| "Advanced Solutions".include?("solutions") | is true | No |
Is not true
This condition is the opposite of the is true condition. It checks that the trigger data IS NOT true. It can also be used to check that the formula provided in the trigger data input field evaluates to false. For example, you can convert string type datapills via string formulas into conditions that evaluates to a boolean.
| Trigger data | Condition/value | Picked up by recipe? |
|---|---|---|
| pill.present? | is not true | No #if pill has a nil or null value or is an empty string "" |
| pill.present? | is not true | No #if pill has a value |
| "Advanced Solutions".include?("Solutions") | is not true | No |
| "Advanced Solutions".include?("solutions") | is not true | Yes |
Is present
This condition will check the trigger data. If there is data present, the trigger event will be picked up by the recipe. If input is null or an empty string, the trigger event will not be picked up by the Recipe.
This condition is valid for all data types, e.g. booleans, string, integers and floats, dates, arrays.
| Trigger data | Condition/value | Picked up by recipe? |
|---|---|---|
| "Advanced Solutions" | is present | Yes |
| 12345 | is present | Yes |
| "" | is present | No |
| nil | is present | No |
Is not present
This condition will check the trigger data. If there is data present, the trigger event WILL NOT be picked up by the recipe. If input is null or an empty string, the trigger event WILL be picked up by the Recipe.
This condition is valid for all data types, e.g. booleans, string, integers and floats, dates, arrays.
| Trigger data | Condition/value | Picked up by recipe? |
|---|---|---|
| "Advanced Solutions" | is not present | No |
| 12345 | is not present | No |
| "" | is not present | Yes |
| nil | is not present | Yes |
Troubleshooting
HTTP errors
400 Bad request. The server could not process the request: malformed, invalid fields or columns, or a field constraint violated. Check field and column naming convention, snake case or camel case, and remember names are case sensitive. Confirm record IDs exist. Check whether a file being sent is too large. Refresh the connection for a fresh token.
401 Unauthorized. Connection credentials are invalid, commonly because they were changed since the connection was made. Go to the Connection, disconnect, and reconnect with the updated credentials.
403 Forbidden. The account lacks permission for the data the action needs. Access to employee records but not salaries, in a recipe that touches salaries, fails here. Use network tracing to identify the record causing it.
⚠️ Network tracing is not available for triggers. A 403 in the trigger has to be diagnosed by building a separate recipe to identify the offending record.
404 Not found. The record cannot be found, commonly from passing an ID from the wrong system, such as an Insightly ID to retrieve a NetSuite record. Verify datapill mapping, that the ID is valid for the application and for the object type, and that the record has not been deleted.
422 Unprocessable entity. Invalid data, usually a conflict. Creating a user whose email already exists returns 422. For creates, check for an existing record with that unique key. For updates, check the record exists. Check reference IDs resolve, and that required fields are not blank.
500 internal server error. The target application failed to return a proper response and the error detail is usually too thin to debug. Wrap the failing action in a Monitor & Handle Errors block, run the job, and read the full error message in the On Error block.
On-premise Agent, No profile found. The profile name in the connection setup does not match the profiles configured on the on-prem agent. Applies to all on-prem connections. Verify the credentials in the OPA config match the connection setup, that the YAML is correctly formed, and that the same profile name is used in both places.
Workato Recipe Error 401 Unauthorized
Workato Job Failure with Greenhouse 422 Error
Trigger errors and warnings
Trigger errors happen when a recipe cannot retrieve events at all: an invalid connection, insufficient permissions on the connected user, a broken API call after a schema change without a schema refresh, an API timeout, or logically incorrect trigger filters such as a null checked against an integer.
A trigger error surfaces as a trigger warning and the recipe keeps polling. No failed jobs appear on the jobs report, because no jobs were created, which is why a recipe can appear idle rather than broken.
⚠️ AppConnect stops a recipe after 60 trigger errors, on the assumption that the fault is critical rather than transient. An email notification is sent.
Infinite loops
An infinite loop is a logic error where an action in a recipe re-triggers that same recipe. Most common in bi-directional syncs, where data moves from application A to B and back to A, and in setups with multiple recipes where one updates an object that triggers another.
Signs: an unexpectedly high transaction count, the recipe triggering when there are no new events in the connected applications, and many duplicate objects created by the recipe.
The fix is trigger filters that stop the re-trigger. Create fields in the connected applications specifically for identifying records synced by AppConnect, and label them unambiguously, such as “Synced by AppConnect”.
⚠️ Do not repurpose a commonly used field as the sync marker. Someone filling it in by hand will cause that record to be filtered out of the trigger.
Rate limiting
Most APIs cap calls within a period, and the limits differ by app. Hitting one throws an error that is usually explicit about being a rate limit. Reduce the number of actions performed against that app, or raise the API rate limit with the app provider if it recurs.
Related to