Routing rules

The JSON rule language for smart links — fields, operators, actions, evaluation order, split tests and validation.

Updated 2026-09-02

Rules are an ordered array stored on the smart link. The first enabled rule whose conditions match decides the destination; if none match, the default resolution applies.

Rule shape#

JSON
{
  "id": "tr-local",
  "name": "Türkiye → local storefront",
  "enabled": true,
  "all": [ { "field": "country", "op": "in", "value": ["TR"] } ],
  "any": [ { "field": "os", "op": "eq", "value": "ios" }, { "field": "os", "op": "eq", "value": "android" } ],
  "action": { "type": "deeplink", "path": "/tr/collections/summer", "fallbackUrl": "https://example.com/tr" }
}

all conditions are AND-ed, any conditions are OR-ed; a rule matches when every all condition holds and at least one any condition holds (when any is present). Max 100 rules, 20 conditions per list.

Fields#

FieldTypeNotes
countryISO-3166 alpha-2From edge geo or the IP pool
region, citystringRegion / city names
continentEU, NA, AS, …
isEUbooleanEU-27 membership
timezoneIANA tzVisitor's timezone when known
ipTypeRESIDENTIAL, MOBILE, DATACENTER, VPN, PROXY, TOR, CDN, SEARCH_BOT, UNKNOWNFrom the IP pool
asnstringAutonomous system number
ipVersion4 or 6
osios, android, windows, macos, linux, other
osVersionnumberMajor.minor as a number, e.g. 17.5
devicemobile, tablet, desktop, tv, console
browserstringsafari, chrome, …
isBotbooleanVerified crawler
isInAppBrowserbooleanInstagram, TikTok, Facebook, Line, WeChat …
languageBCP-47 primary tagFrom Accept-Language
referrerHosthostnameFrom Referer
hour0–23In the rule's timezone if set, else the visitor's
weekday0–6 (Sunday = 0)
dateYYYY-MM-DD
param.<name>stringAny query parameter, e.g. param.utm_source

Operators#

in, notIn, eq, neq, gte, lte, between (value: [min, max]), matches (regex), startsWith, exists, notExists.

Actions#

typeFieldsEffect
redirecturl, passParams?, appendClickId?302 to a URL
storeiosUrl?, androidUrl?, fallbackUrl?OS-aware store redirect (Play gets the Install Referrer)
deeplinkpath, scheme?, universalLinkBase?, iosUrl?, androidUrl?, fallbackUrl?, schemeTimeoutMs?Universal link → custom scheme interstitial → store, and stores path for deferred deep linking
splitvariants: [{ weight, label?, action }] (2–10)Deterministic A/B split by visitor bucket (stable per IP + UA + slug)
blockstatus? (403 / 404 / 410), message?Stop the request
passthroughSkip remaining rules and use the link defaults

Example rule set#

rules
[
  {
    "id": "tr-local",
    "name": "Türkiye → local storefront",
    "all": [
      {
        "field": "country",
        "op": "in",
        "value": [
          "TR"
        ]
      }
    ],
    "action": {
      "type": "deeplink",
      "path": "/tr/collections/summer",
      "fallbackUrl": "https://example.com/tr"
    }
  },
  {
    "id": "eu-consent",
    "name": "EU visitors → consent landing",
    "all": [
      {
        "field": "isEU",
        "op": "eq",
        "value": true
      },
      {
        "field": "os",
        "op": "notIn",
        "value": [
          "ios",
          "android"
        ]
      }
    ],
    "action": {
      "type": "redirect",
      "url": "https://example.com/eu/landing"
    }
  },
  {
    "id": "istanbul-night",
    "name": "Istanbul 00:00–06:00 → night campaign",
    "all": [
      {
        "field": "city",
        "op": "eq",
        "value": "Istanbul"
      },
      {
        "field": "hour",
        "op": "between",
        "value": [
          0,
          6
        ]
      }
    ],
    "action": {
      "type": "redirect",
      "url": "https://example.com/night"
    }
  },
  {
    "id": "old-ios",
    "name": "iOS < 15 → web (app unsupported)",
    "all": [
      {
        "field": "os",
        "op": "eq",
        "value": "ios"
      },
      {
        "field": "osVersion",
        "op": "lte",
        "value": 14.9
      }
    ],
    "action": {
      "type": "redirect",
      "url": "https://example.com/app-web"
    }
  },
  {
    "id": "tiktok-ab",
    "name": "TikTok traffic A/B",
    "all": [
      {
        "field": "param.utm_source",
        "op": "eq",
        "value": "tiktok"
      }
    ],
    "action": {
      "type": "split",
      "variants": [
        {
          "weight": 50,
          "label": "A",
          "action": {
            "type": "deeplink",
            "path": "/product/a"
          }
        },
        {
          "weight": 50,
          "label": "B",
          "action": {
            "type": "deeplink",
            "path": "/product/b"
          }
        }
      ]
    }
  },
  {
    "id": "vpn-block",
    "name": "Datacenter / Tor → block",
    "all": [
      {
        "field": "ipType",
        "op": "in",
        "value": [
          "DATACENTER",
          "TOR"
        ]
      }
    ],
    "action": {
      "type": "block",
      "status": 404
    }
  }
]

Evaluation details#

  • Rules run at the edge in under a millisecond; geo and IP-type come from request.cf or the pool lookup.
  • Deep-link actions on desktop fall back to fallbackUrl, then the link's webFallbackUrl.
  • A visible url= parameter bypasses rules entirely (Google transparency).
  • The matched rule id and action type are stored on the click (routeRuleId, routeAction) and are available as report dimensions.

Validation and simulation#

Rules are validated with the schema on save (dashboard, update_smart_link_routing) and can be checked without saving via validate_routing_rules. Simulate visitors with test_smart_link_route or POST /api/dashboard/route-test:

Simulator request
{ "slug": "summer", "country": "TR", "os": "ios", "params": "utm_source=tiktok", "in_app_browser": true }
Decision
{ "decision": { "action": { "type": "deeplink", "path": "/tr/collections/summer", "fallbackUrl": "https://example.com/tr" }, "ruleId": "tr-local", "label": "Türkiye → local storefront", "location": "https://links.yourbrand.com/tr/collections/summer?wc_click_id=SIMULATED" } }

ruleId is null and label is "default" when no rule matched. location is the URL the visitor would be sent to (for deep-link actions on iOS/Android the store or universal link; the interstitial is used for in-app browsers).