Workflow help

Workflows are automatic rules. You set them up once, and from then on the app handles the busywork — texting tenants, issuing door codes, sending reminders. No coding needed. This guide walks you through everything, with examples you can copy.

What's a workflow?

Think of a workflow as an "if this happens, then do that" rule. You decide what should trigger it and what should happen, and the app does the work for you every time.

A few real examples:

Every workflow has a status. You'll see it in the workflow list:

The three parts

Every workflow has three pieces. You build them in this order in the workflow builder:

PartWhat it does
TriggerThe thing that starts the workflow — for example, "a lease was just created" or "every hour, check for upcoming check-ins."
FiltersConditions that narrow it down. Without filters, the workflow runs every time the trigger happens. Add a filter to only run for, say, vacation rentals — or only for one specific property.
StepsWhat you actually want to happen. Each step does one thing — send a text, issue a door code, etc. You can add as many steps as you want, and they run in order.

Instant triggers

These run the moment something happens in the app. As soon as you (or someone on your team) creates, updates, or deletes one of these things, any matching workflow fires right away.

What you do in the appTriggers this
Create a lease Lease created
Edit any field on a lease Lease updated
Delete a lease Lease deleted
Add a contact (including from the lease form)Contact created
Edit a contact Contact updated
Delete a contact Contact deleted
Anything that creates an in-app notification (a chat message, a billing event, etc.)Notification created

Tip: The Notification created trigger fires for every in-app notification. Use a filter on type_name (for example, type_name eq chat_message) to react only to a specific kind.

The Schedule trigger

Some things happen because of time, not because someone clicked a button. "Send a reminder 3 hours before check-in." "Email me a summary every Monday." "Notify me when a lease ends today."

For all of those, use the Schedule trigger. The app checks once an hour — at the top of every hour — and looks for records that match your filters. If it finds any, your workflow runs once for each match. It also remembers which records it already handled, so you won't get duplicate reminders.

You can use Schedule on Lease or Contact records. So "find leases ending today" or "find contacts whose anniversary is this week" both work the same way.

How often it checks

Schedule triggers have a frequency setting. Here's what each one means:

SettingWhat it means
hourly Check every hour, on the hour.
daily Check at the next hourly check, but only once per day. After it runs, it waits at least 24 hours before running again.
weekly Same idea — runs at most once a week.
monthly Runs at most once a month.

If you're not sure which to pick, hourly is usually the right answer — your filters control what actually fires anyway. Use the others when you specifically want a lower volume (like a daily summary email).

Conditions you can use

Filters narrow down when your workflow runs. Add as many as you want — they all have to be true for the workflow to fire.

ConditionNeeds a value?Means
equals yes Matches exactly
not equals yes Doesn't match exactly
contains yes The value contains the text you provided (capitalization doesn't matter)
not contains yes The value does not contain the text
is set The field has any value (not blank)
is empty The field is blank
is before now The date is in the past
is after now The date is in the future
within next e.g. 3h, 1d, 2w, 1mo The date is coming up within that amount of time
within last same The date was within that amount of time ago

For the within next and within last conditions, the value tells the app how far ahead or back to look:

Filter examples

Here are some common goals and the filter that does it:

What you wantFilter
Only run for one specific property property equals "<the property's ID>"
Only run for leases that have already endedend_date is before now
Send a checkout reminder ~3 hours before end_date within next 3h
Skip leases without a phone number contact_phone is set
Only fire for company emails email contains "@company.com"

Adding steps

Click + Add step in the workflow builder. Pick what kind of action you want (Smart Lock, Text Message, etc.), then pick the specific action.

Each step has its own form to fill in. The fields depend on the action — for a text message, you'll need a phone number and a body. For a smart-lock code, you'll need the lock and a date range. The next section explains placeholders, which let you fill those fields with information from the trigger automatically.

What each action does

Smart Lock · Issue access code

Creates a new door code on the lock and saves it in the app. Useful right after a tenant signs a lease.

FieldRequired?Notes
lock_id yes Which lock to add the code to. Find this on the lock's page in the app.
property yes The property this is for.
code no 4 to 9 digits. Leave it blank and the app picks a random 6-digit code for you. That's usually what you want.
valid_from yes When the code starts working. Usually {{trigger.start_date}} from the lease.
valid_to yes When the code stops working.
lease_id no Connects the code to a lease so it shows up in the lease's record.

Smart Lock · Update access code

Changes the start/end dates on the door code(s) for a lease. Use this if a lease's dates change and you want the code to stay in sync.

Smart Lock · Revoke access code

Removes the code from the lock and from the app. The tenant won't be able to use it anymore. This is usually the last step you'd add to a "lease deleted" workflow.

Text Message · Send text

Sends an SMS through your Twilio account. The phone number is auto-formatted, so a US number entered as 2146366019 will be sent as +12146366019. The body can include placeholders (see below).

Notification · Send notification

Creates an in-app notification — the kind that shows up on the bell icon at the top of the app. Useful for nudging yourself or your team about something important without sending a text.

FieldRequired?Notes
type_name yes One of: chat_message, lease_starting_soon, lease_ending_soon, payment_received, payment_failed, workflow_completed, workflow_failed, system_announcement. The type controls the icon and default color.
title yes The headline shown on the notification (one line). Placeholders work — e.g. Lease ending for {{trigger.contact_first_name}}.
body no Longer description shown under the title. Placeholders work.
severity no One of info, success, warn, error. Overrides the type's default color (e.g. mark a payment failure red).
user_id no Send to one specific team member. Leave it blank to send to everyone on your account. That's the usual choice.
entity_type / entity_idnoLink the notification to something in the app — for example, entity_type = lease and entity_id = {{trigger.lease_id}}. Used so the bell can clear the badge when you open the related screen.
dedupe_key no Prevents duplicate notifications. If you fire the same workflow many times in a row, set this to something stable (like the lease id) and only one notification is created until it's read.

Placeholders

When your workflow runs, you'll usually want to pull in info from whatever triggered it. The tenant's name. The lease's start date. The phone number on file. Instead of typing all that out by hand, you use placeholders.

A placeholder looks like this:

{{trigger.contact_first_name}}

The double curly braces tell the app "replace this with real data when the workflow runs." So if your text body is:

Hi {{trigger.contact_first_name}}, welcome to your stay!

And the workflow fires for a tenant named Sarah, the actual text she gets is:

Hi Sarah, welcome to your stay!

The two main kinds of placeholders are:

PlaceholderWhat it pulls in
{{trigger.something}} A piece of info from whatever started the workflow — the lease, the contact, etc.
{{steps.s1.something}} A piece of info from a previous step. Step 1 is steps.s1, step 2 is steps.s2, and so on.

For example, after step 1 generates a door code, step 2 (the text message) can include it like this:

Your door code is {{steps.s1.code}}.

You don't have to memorize the placeholder names. When you're editing a step, the right-hand panel of the builder shows every placeholder that's available, with one-click insert. Just click into the field you want to fill, then click the placeholder. Done.

Functions

Sometimes you don't just want to drop in a value as-is — you want to change it a little. Format a date so it reads "April 29, 2026" instead of the raw 2026-04-29T16:00:00.000Z. Make a name ALL CAPS for a header. Round a price to 2 decimals.

That's what functions are for. Each one belongs to a category — string (for text), number, or date — and you call it like this:

{{date.format(trigger.start_date, 'MMMM D, YYYY')}}

That formats the lease's start date as "April 29, 2026". The first thing inside the parentheses is what to format; the second is how you want it formatted (see date formats below).

You'll see all the available functions in the right-hand panel of the workflow builder, grouped by category. Click any one to insert it into the field you're editing.

Here's the full list:

Loading…

Date formats

When you use date.format, you tell it how you want the date to look using letters as placeholders. Mix and match these to get exactly the format you want — anything that isn't a placeholder (like dashes, spaces, or commas) shows up as-is.

LettersWhat they showExample
YYYY or YY year 2026 or 26
MM or M month as a number04 or 4
MMMM or MMM month name April or Apr
DD or D day of month 29
dddd or ddd weekday Wednesday or Wed
HH or H hour (24-hour) 16
mm minute 00
ss second 00

Some examples:

One thing to know about times. Dates are always shown in UTC time, not your local time zone. So if a lease is set to start at 4:00 PM UTC, the formatted date will read 16:00 — even if you're in Eastern Time, where that's actually noon. If your dates look "off by a few hours," this is usually why. Account-timezone support is on the roadmap.

Combining functions

Functions can be plugged into each other. You only need one outer pair of {{ }} — the rest goes inside.

For example, "format the day after the lease starts":

{{date.format(date.addDays(trigger.start_date, 1), 'DD-MM-YYYY')}}

Read it inside-out: first date.addDays(...) takes the start date and adds 1 day. Then date.format(...) takes that result and formats it. You can keep nesting like this as much as you need.

A few more examples:

{{string.upper(string.concat(trigger.contact_first_name, ' ', trigger.contact_last_name))}}
→ "SARAH JOHNSON"

{{number.round(number.divide(steps.s1.total, steps.s1.count), 2)}}
→ "12.34"

{{string.concat('Hi ', trigger.contact_first_name, ', code: ', steps.s1.code)}}
→ "Hi Sarah, code: 482910"

Checking what ran

Every time a workflow fires, it leaves a record. There are a few places to look:

Each run shows its status, what action ran (or what failed), when it happened, and how long it took. Click "Trigger payload + step results" on a row to expand the details — useful if you're investigating why something didn't quite do what you expected.

Notice the ↻ rerun button on each row. It re-runs that specific entry with its original data. Helpful for "I fixed the typo, now retry that failed text" or "send that confirmation again, the tenant didn't get it."

Don't need old failures cluttering the view? Open the runs modal, switch to the Failed tab, and click Clear failed. The records get cleaned up. (Your central error log keeps a copy either way.)

When something doesn't work

You'll occasionally see runs marked as failed. Here are the most common reasons and what to check:

What you'll seeWhat it meansWhat to try
Action 'Send text' requires 'to' mappingYou forgot to fill in the phone number field.Open the step, fill in the to field with {{trigger.contact_phone}} or a static number.
'From' +1... is not a Twilio phone numberYour Twilio settings aren't quite right.Check that TWILIO_FROM_NUMBER in your config matches a number you actually own in Twilio.
Invalid dateOne of the date functions got something it couldn't read.Make sure the field you're using actually has a date in it. Check the live preview in the step editor.
Unknown function: date.formaatTypo in a function name.Check spelling, or use the function picker on the right side of the step editor instead of typing.
Lock not foundThe lock ID you mapped doesn't exist or isn't on your account.Re-copy the lock ID from the lock's page.

The live preview is your friend. When you're editing a step, the field you're typing in shows a small preview underneath of what the value will look like when the workflow actually runs. If you see ⚠ red text, something's wrong — fix it before saving and you'll save yourself a failed run.

Example: onboard a tenant

Goal: When a new lease is created, automatically issue a door code and text it to the tenant.

SettingValue
Trigger Lease / Lease created
Filters (none — fire for every new lease)
Step 1 Smart Lock / Issue access code
  lock_id (the smart lock's ID — find it on the lock's page)
  property {{trigger.property}}
  valid_from {{trigger.start_date}}
  valid_to {{trigger.end_date}}
  lease_id {{trigger.lease_id}}
  code (leave blank — the app generates one)
Step 2 Text Message / Send text
  to {{trigger.contact_phone}}
  body Hi {{trigger.contact_first_name}}, your code is {{steps.s1.code}}. Valid {{date.format(trigger.start_date, 'MMM D')}} – {{date.format(trigger.end_date, 'MMM D')}}.

The text the tenant receives will look like: "Hi Sarah, your code is 482910. Valid Apr 29 – May 3."

Example: pre-arrival reminder, ~4 hours before check-in

Goal: A friendly text to upcoming guests about 4 hours before their check-in time.

SettingValue
Trigger Lease / Schedule
Frequency hourly
Filter start_date within next 4h
Step Text Message / Send text
  to {{trigger.contact_phone}}
  body Hi {{trigger.contact_first_name}}, looking forward to your arrival at {{date.format(trigger.start_date, 'h:mma')}}!

Each guest gets the message exactly once — even though the app checks every hour, it remembers who it already texted.

Example: when a lease is deleted

Goal: When a lease is deleted, automatically revoke any door codes that were tied to it.

SettingValue
Trigger Lease / Lease deleted
Filter (none)
Step Smart Lock / Revoke access code
  lease_id {{trigger.lease_id}}

The app remembers which codes were tied to that lease before the delete happens, so the revoke action can find them and clean them up. No extra config needed — it just works.

URManager workflow help. Last reviewed 2026-04-29.