Triggering an automation after a two hour delay

Following on from my post yesterday I received this question:

I have an app and when I set a select field to x, I want it to stay set to x for 2 hours, then change to y which would trigger new automation.

So this is not a beginner question in my mind however here goes.

First off, you can’t schedule an automation to run exactly 2 hours later or delay one for 2 hours, and the shortest amount of time we can automatically trigger an automation to check a state is 15 mins. This means it might run up to 15 minutes later than expected. If this is important then I believe an external tool would be required to monitor the time.

Second, the follow-on automation doesn’t need to be triggered by the select field change - it can be triggered by the same automation that sets the select field to y.

Finally, as with everything I am sure there are multiple ways to achieve this. I have tried to pick one that is as simple as possible. If anyone else has an easier way then please do let us know.

First

The first thing we need to do is give something to check against so we can check if the 2hrs is up. Logically this would be a date/time field with the time set to +2hrs from the time the field was set to x. However, when using ‘before date’ searches or filters in Automations, Tape only considers the date and not the time which means that approach won’t work.

NOTE: I thought I would be able to use current trigger date/time however that didn’t seem to work either. We could build a script that checks date and time if we really wanted.

However not to worry as we can really easily convert date/time into a number then we can just compare the current time with our target and if the current time is larger than the target perform the actions required.

Record

For this to work we need a field to store the Date/time in. As I said above this is actually going to be a number so a hidden number field will do.

Automation one

The automation gets triggered when the select field is changed and it adds 2hrs to the current time and then converts it to UNIX time (which is a number) and adds that to our hidden number field.

date_fns.getUnixTime(date_fns.addHours(date_fns.parseISO(current_datetime_local),2))

And this gives us a number like :point_down:

Automation two

  1. We will use an every 15 mins trigger
  2. which will first create a UNIX time for the current time.
  3. We search for records which have the correct select field setting and have a timestamp that is a lower number than our current one.
  4. We then update the record/s by changing the select field and clearing the timeStamp field.
  5. Finally we can call our follow on automation.

Alternative

As I mentioned earlier, there are several ways to achieve this. I’ve tried to choose a simple and reliable approach. However an alternative way to do this is to wrap the bulk of the automation in a conditional so we minimise unnecessary steps:


This way we search for any records with the correct status first then only if there are any do we process further. It’s a little more complex, but probably kinder on the system. I think the added complexity is worth it.

Enhancements

Safety checks on Start

Also as single select fields it is possible to (re-set) a timer by mistake so we can build in some safety by checking for the existence of a time stamp and then checking to see if the timestamp is older than the now rather than still waiting to trigger:


As can be seen we are starting to add a fair bit of complexity now which may not be needed, however you can see the checks working here :point_down:
CleanShot 2025-11-06 at 07.37.12

Completed time

We can add a calculation field that displays the time the process will be completed - handy for those ‘time blind’ amongst us that will either think only 5 mins has passed and in fact it has been 5 hrs or more relevant in this case think 5hrs have passed and it’s only been 5 mins



The calculation field:

@timeStamp ? date_fns.fromUnixTime(@timeStamp) : '';

Wait time

We can add one more possible enhancement to this which is the ability to set the wait time and we can do this by adding a number field to the record and then getting it in our automation and falling back to 2hrs if it is empty:


CleanShot 2025-11-06 at 08.02.52

Hopefully that gives you a clear way to handle timed transitions in Tape. It’s not perfect, but it’s practical and reliable — and as always, I’d love to hear if anyone’s found a cleaner solution.

3 Likes

This is of course brilliant like everything that Jason consults on, but I just wanted to add one additional piece for consideration.

Yes, the “cron” handler in Tape only runs every 15 minutes. However, if you go to https://cron-job.org, it’s a free tool you can sign up for which sends webhooks to any endpoint on whatever schedule you want, including every minute. So technically, you could build the same thing, except have a cron running every 5 minutes or even every 1 minute, seeking out items that have passed the timeframe so they can be run. That way, the worst that will happen is that something is 1 minute after you expect it rather than 15.

2 Likes

I didn’t know about https://cron-job.org/ that is a really interesting site and will definitely be added tp my tool box - thanks :slight_smile:

1 Like

AND IT’S FREEEEEE!! :slight_smile:

2 Likes

For those who are interested in how this could be modified to work with cron-job.org here it is be warned this definitely pushes the solution out of the basic!

We can leave everything the same on the start only we add a script block:

const baseUrl = 'https://api.cron-job.org/jobs'
const api = collected_api_key_field_apikey_value;
const activateTime = date_fns.addMinutes(date_fns.parseISO(current_datetime_local), var_wait);
const expiresAt = date_fns.format(date_fns.addMinutes(activateTime, 5),'yyyyMMddHHmmss'); // YYYYMMDDhhmmss
const hours = [date_fns.getHours(activateTime)]; // Gets the hour from our activateTime
const minutes = [date_fns.getMinutes(activateTime)]; // Gets the minutes from our activateTime
const dayOfMonth = [date_fns.getDate(activateTime)]; // Specific day
const month = [date_fns.getMonth(activateTime) + 1]; // Specific month (getMonth is 0-indexed)
const ref = current_record_id; // Just puts the record_id in a variable
const title = `${cascade_rise___service_app_field_name_value} - ${current_record_id} - ${expiresAt}`; // This title helps when you go to cron-job dashboard

const jobPayload = {
  job: {
    url: "https://tapeapp.com/api/catch/eyJhbGci2qUrHqvgJpyYt7RLU",
    enabled: true,
    requestMethod: 1,
    title: title,
    extendedData: {
      body: JSON.stringify({
        message: ref
      })
    },
    schedule: {
      timezone: "Europe/London",
      expiresAt: expiresAt,
      hours: hours,
      minutes: minutes,
      mdays: dayOfMonth,  
      months: month,       
      wdays: [-1]     
    }
  }
};

const response = await http.put(baseUrl, {
    headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${api}`
    },
    data: jobPayload
});

console.info(JSON.stringify(response, null, 2));

this will set up a cron job that will send the record_id into a waiting webhook the exact number of minutes that as been set on the record.

The waiting webhook automation can then look like:


However as we have already blown the low-code out of the water it may as well look like:

Which means you don’t need to do a search you can just update the relevant record directly and if you want to call an automation you can use the:

tape.Workflow.triggerWorkflow(545355,{
    triggered_on_record_id: record
})

Thanks to @andrew.cranston for the top tip :slight_smile:

1 Like