Smart routing (Business+)
Smart routing lets one short link resolve to different destinations based on rules
you attach to it — by geography, device, time of day, or an A/B split. Rules are
managed with a JWT (they are account-management operations) under the link's
/routing sub-resource.
Reads and deletes stay available even after a downgrade, so you can inspect and clean up existing rules; creating rules requires the Business tier or above.
Rule types
conditions is a JSON object whose shape depends on type:
type | Routes on | Example conditions |
|---|---|---|
geo | Visitor country/city | { "countries": ["US", "CA"], "cities": ["Austin"], "excludeCountries": ["RU"] } |
device | Device type | { "devices": ["mobile", "tablet"] } (types: mobile, tablet, desktop, bot) |
time | Time window | { "timezone": "UTC", "startTime": "09:00", "endTime": "17:00", "daysOfWeek": [1,2,3,4,5] } |
ab_test | A/B split | { "percentage": 50 } — matches ~50% of visitors |
For geo, country codes are matched case-insensitively; for time, startTime /
endTime are HH:mm (overnight ranges are allowed) and daysOfWeek is 0=Sunday …
6=Saturday. For ab_test, assignment is deterministic per visitor IP — a given
visitor always lands in the same bucket, so a rule with percentage: 50 consistently
serves the same variant to the same person.
Create a rule
curl -X POST https://api.short.leonai.io/api/v1/links/<linkId>/routing \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
-d '{
"name": "US shoppers",
"type": "geo",
"priority": 10,
"conditions": { "countries": ["US"] },
"destinationUrl": "https://example.com/us",
"weight": 100,
"enabled": true
}'
REST: POST /api/v1/links/:linkId/routing
| Field | Notes |
|---|---|
type | geo | device | time | ab_test (required) |
conditions | JSON object interpreted per type (required) |
destinationUrl | Where matching traffic goes (required) |
priority | 0–1000; higher priority rules are evaluated first (default 0) |
weight | 0–100 (default 100). Present on the model; A/B splitting is driven by conditions.percentage, not weight |
name | Optional label |
enabled | Default true |
Manage rules
# List all rules on a link
curl https://api.short.leonai.io/api/v1/links/<linkId>/routing \
-H "Authorization: Bearer <jwt>"
# Get one rule
curl https://api.short.leonai.io/api/v1/links/<linkId>/routing/<ruleId> \
-H "Authorization: Bearer <jwt>"
# Update a rule
curl -X PATCH https://api.short.leonai.io/api/v1/links/<linkId>/routing/<ruleId> \
-H "Authorization: Bearer <jwt>" -H "Content-Type: application/json" \
-d '{ "enabled": false }'
# Delete a rule
curl -X DELETE https://api.short.leonai.io/api/v1/links/<linkId>/routing/<ruleId> \
-H "Authorization: Bearer <jwt>"
How resolution works
On each redirect, enabled rules are evaluated in priority order (highest first). The
first rule whose conditions match wins and its destinationUrl is served. If no rule
matches, the link's default longUrl is used.