URL pattern matching

Userflow supports an easy-to-use, yet powerful URL pattern for matching pages in your app. URL pattern matching is useful when auto-starting flows, progressing to the next step, or marking a checklist task as completed.


  • Only supply parts of the URL you care about. Parts left out will match anything. E.g. if you don’t care about the domain, then leave it out.
  • * is a wildcard, matching anything.
  • :segment (a colon followed by any word) matches a single dynamic segment.
  • You can supply multiple patterns to include or exclude. The current page URL must match at least one of the included patterns, and must not match any of the excluded patterns.

Exact path (no subpaths) on any domain


Matches any domain, with exactly the given path.

  • Matches:
    • https://example.com/app/dashboard
    • http://example.com/app/dashboard
    • https://other-example.com/app/dashboard
  • Does not match:
    • https://example.com/app
    • https://example.com/app/dashboard/sub
    • https://example.com/app/dashboard/sub/page

Including subpaths


* matches everything (including nothing).

  • Matches:
    • https://example.com/app
    • https://example.com/app/projects
    • https://example.com/app/projects/1
    • https://other-example.com/app
    • https://example.com/applications
  • Does not match:
    • https://example.com/settings

Only subpaths


Note the / before the * .

  • Matches:
    • https://example.com/app/projects
    • https://example.com/app/projects/1
    • https://other-example.com/app/projects
  • Does not match:
    • https://example.com/app
    • https://example.com/applications

Dynamic path segment


:project matches a single path segment (anything but / ). You can use any word (consisting of letters a-z, numbers and underscores) in place of project - it’s not used for anything.

  • Matches:
    • https://example.com/projects/1/details
    • https://example.com/projects/2/details
  • Does not match:
    • https://example.com/projects/1/details/edit
    • https://example.com/projects/1/history

Mix dynamic path segment and subpaths

  • Matches:
    • https://example.com/app/apple/projects/1
    • https://example.com/app/banana/projects/1
    • https://example.com/app/banana/projects/1/details
  • Does not match:
    • https://example.com/app/apple/projects

Specific domain on any path

  • Matches:
    • https://app.com/
    • https://app.com/any/path
  • Does not match:
    • https://www.app.com/
    • https://not-app.com/

Dynamic subdomain

  • Matches:
    • https://www.app.com/
    • https://www.app.com/any/path
    • https://dashboard.app.com/
  • Does not match:
    • https://app.com/
    • https://multiple.levels.app.com/
    • https://www.not-app.com/

Mix domain and path

  • Matches:
    • https://dashboard.example.com/app/apple/projects
    • https://dashboard.example.com/app/apple/projects/1

URL parts

URLs consist of the following parts:

\___/   \________/\_________________/ \___________/ \____/
  |         |              |                |          |
scheme    domain          path            query     fragment

When writing URL patterns in Userflow, all parts are optional. If a part is left out, it’ll match anything.

Each part has slightly different rules.


The scheme is what comes before :// in a URL.


The scheme is almost always either http or https . You only need to specify scheme in your URL patterns if you care about whether the user is on http or https . Usually your server takes care of redirecting users to one or the other, meaning in most cases you can leave it out of your pattern.

  • https://example.com matches any URL on https://example.com but not on http://example.com .
  • example.com matches any URL on both https://example.com and http://example.com .


The domain part is what comes after :// and before the path’s first / .


Dynamic subdomain

To match dynamic subdomains (e.g. apple.app.com and banana.app.com ), use a colon and an arbitrary name such as :company.app.com . :company will match a non-empty subdomain, meaning it will not include . . The name after : does not matter and is currently not used for anything.

:company.app.com and :subdomain.app.com are equivalent and will match:

  • apple.app.com
  • banana.app.com

They will not match:

  • app.com (missing subdomain)
  • more.apple.app.com (more subdomains)

Wildcard domains

You can also use * as a wildcard character to match any number of subdomains.

*.app.com will match both one.app.com and one.two.app.com , but not app.com .

*app.com will match app.com , but will also match myapp.com .


The path part is what comes after the domain and before the query (starting with ? ), or the fragment (starting with # ) if there’s no query.


Important: Paths MUST start with a slash ( / ). The URL pattern app/projects will consider app as the domain, and will match full URLs such as https://app/projects (which doesn’t really make sense). The right pattern here would be /app/projects .

Dynamic path segments

To match dynamic path segments (e.g. /app/apple/projects and /app/banana/projects ), use a colon and an arbitrary name such as /app/:company/projects . :company will match a non-empty path segment, meaning it will not include / . The name after : does not matter and is currently not used for anything.

/app/:company/projects and /app/:slug/projects are equivalent and will match:

  • /app/apple/projects
  • /app/banana/projects

They will not match:

  • /app/projects (missing path segment)
  • /app/apple/projects/1 (deeper path)

Wildcard paths

You can also use * as a wildcard character to match nested paths.

/app/* will match both /app/one and /app/one/two , but not /app .

/app* will match /app , but will also match /applications .

/*/projects will match /app/projects and /app/one/two/three/projects .

You can mix wildcards and dynamic path segments at will. Example: /*/customers/:customer/invoices/:invoice/*


The query is what comes after ? and before the fragment (starting with # ), if any.


A URL can contain multiple query parameters separated by ampersands ( & ), as in ?fruit=apple&tab=analytics . Query parameters are matched one by one. Order of query parameters does not matter. Extra, non-matched query parameters, are not considered.

?fruit=apple&tab=analytics will match:

  • ?fruit=apple&tab=analytics
  • ?tab=analytics&fruit=apple (different order is OK)
  • ?other=test&fruit=apple&another=test&tab=analytics (extra parameters are ignored)

But will not match:

  • ?fruit=apple ( tab parameter is missing)
  • ?fruit=banana&tab=analytics (wrong value for fruit parameter)

Wildcard query parameter values

You can use * to match any value in individual query parameters. It’s required that the query parameter is present though.

?param=a* will match:

  • ?param=apple
  • ?param=alphabet

It will not match:

  • ?param=banana (does not start with a )
  • ?param (no value)
  • ?param2=apple (wrong parameter name)

?param=* will match:

  • ?param=apple
  • ?param (no value, but present)

It will not match:

  • ?param2=apple (wrong parameter name)


The fragment is what comes after # .


Since the fragment is often used like a path in single-page applications, fragment obeys the same rules as Paths .

To match a dynamic fragment segment: #/projects/:project/dashboard .

Using wildcard: #/projects/:project/dashboard/* .

Got questions? We're here for you!

The best way to get help is to
We usually reply within 5 minutes
You can also send an email to support@getuserflow.com
We usually reply within a few hours