Links
Manage smart links via the API. All endpoints require authentication.
Endpoints
| Method | Path | Description |
|---|---|---|
POST | /link/create | Create a link |
PUT | /link/update | Update a link |
DELETE | /link/delete | Delete a link |
POST | /links/bulk-create | Bulk create links |
Rule Object
Rules define where a link redirects. Every link must have at least one rule with isDefault: true — this is the fallback destination when no other rule's conditions match.
Non-default rules are evaluated in order. The first rule whose conditions all match wins. If no rule matches, the default rule is used.
Rule IDs are generated automatically by the server — do not include id in your request.
Constraints:
- Exactly one rule per link must have
isDefault: true - The default rule is a pure fallback — it cannot have any conditions (
country,city,device,os,browser,timeStart,timeEnd,daysOfWeek). Conditions are only allowed on non-default rules timeStartandtimeEndmust be provided together or both omitted
| Field | Type | Required | Description |
|---|---|---|---|
targetUrl | string | ✅ | Destination URL. Any valid URL including deep links (e.g. youtube://, myapp://) |
isDefault | boolean | ✅ | Exactly one rule per link must have true |
country | string[] | — | ISO 3166-1 alpha-2 country codes (e.g. ["UA", "PL"]). Only visitors from these countries match |
city | string[] | — | City names (e.g. ["Kyiv", "Warsaw"]). Requires country to be set — cannot use city without country |
device | string[] | — | Device types. Accepted values: "desktop", "mobile", "tablet", "console", "smarttv", "wearable", "embedded" |
os | string[] | — | Operating systems. Accepted values: "Windows", "macOS", "Linux", "iOS", "Android" |
browser | string[] | — | Browsers. Accepted values: "Chrome", "Firefox", "Safari", "Edge", "Opera", "Brave" |
timeStart | string | — | Start of active time window in "HH:MM" format (e.g. "09:00") |
timeEnd | string | — | End of active time window in "HH:MM" format (e.g. "18:00") |
daysOfWeek | number[] | — | Days when the rule is active. 0 = Sunday … 6 = Saturday (e.g. [1,2,3,4,5] for weekdays) |
Full rule example (all optional condition fields):
{
"targetUrl": "https://example.com/ua-mobile",
"isDefault": false,
"country": ["UA", "PL"],
"city": ["Kyiv", "Warsaw"],
"device": ["mobile"],
"os": ["iOS", "Android"],
"browser": ["Chrome", "Safari"],
"timeStart": "09:00",
"timeEnd": "21:00",
"daysOfWeek": [1, 2, 3, 4, 5]
}
Create a link
POST /link/create
Request
POST https://api.revolink.link/apiv1/link/create
Content-Type: application/json
X-Api-Key: rvlnk_your_api_key_here
Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
workspaceId | string | ✅ | Workspace ObjectId |
name | string | ✅ | Display name for the link |
rules | Rule[] | ✅ | Routing rules. At least one rule with isDefault: true required |
description | string | — | Internal description (not shown to visitors) |
slug | string | — | Custom URL slug. Auto-generated if omitted. Lowercase letters, numbers, hyphens only. Max 100 chars |
domain | string | — | Custom domain hostname (e.g. "go.mycompany.com"). Must be a verified domain in the workspace. Defaults to rvlnk.link |
active | boolean | — | Default: true |
clicksLimit | number | — | Positive integer. No limit if omitted |
expirationDate | string | — | ISO 8601 datetime. Must be in the future |
tagIds | string[] | — | Array of tag ObjectIds |
description | string | — | Max 1000 characters |
Minimal example
curl -X POST https://api.revolink.link/apiv1/link/create \
-H "Content-Type: application/json" \
-H "X-Api-Key: rvlnk_your_api_key_here" \
-d '{
"workspaceId": "664abc000000000000000001",
"name": "My link",
"rules": [
{
"targetUrl": "https://example.com",
"isDefault": true
}
]
}'
Full example (all fields)
curl -X POST https://api.revolink.link/apiv1/link/create \
-H "Content-Type: application/json" \
-H "X-Api-Key: rvlnk_your_api_key_here" \
-d '{
"workspaceId": "664abc000000000000000001",
"name": "Summer campaign",
"description": "Main landing for summer 2025 promo",
"slug": "summer-2025",
"domain": "go.mycompany.com",
"active": true,
"clicksLimit": 10000,
"expirationDate": "2025-09-01T00:00:00Z",
"tagIds": ["664abc000000000000000010", "664abc000000000000000011"],
"rules": [
{
"targetUrl": "https://example.com/ua-mobile",
"isDefault": false,
"country": ["UA"],
"device": ["mobile"],
"os": ["iOS", "Android"],
"timeStart": "08:00",
"timeEnd": "22:00",
"daysOfWeek": [1, 2, 3, 4, 5]
},
{
"targetUrl": "https://example.com/de",
"isDefault": false,
"country": ["DE", "AT", "CH"],
"device": ["desktop"],
"browser": ["Chrome", "Firefox"]
},
{
"targetUrl": "https://example.com",
"isDefault": true
}
]
}'
Response 200 OK
{
"message": "Link \"Summer campaign\" successfully created.",
"id": "664abc123def456789000001",
"slug": "summer-2025",
"fullLink": "https://go.mycompany.com/summer-2025"
}
Error Responses
| Status | Body | Cause |
|---|---|---|
400 | {"error": "Name is required."} | name field is missing |
400 | {"error": "rules must be a non-empty array."} | rules is missing or empty |
400 | {"error": "Domain is not available for this workspace."} | Provided domain is not a verified domain of this workspace |
403 | {"licenseLimitReached": true, ...} | Plan link or rules quota reached |
409 | {"error": "This slug is already taken."} | Provided slug is already in use on this domain |
Update a link
PUT /link/update Only fields you provide are changed — all other fields remain unchanged.
Request
PUT https://api.revolink.link/apiv1/link/update
Content-Type: application/json
X-Api-Key: rvlnk_your_api_key_here
Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
workspaceId | string | ✅ | Workspace ObjectId |
linkId | string | ✅ | ObjectId of the link to update |
name | string | — | New display name |
description | string | — | New description |
rules | Rule[] | — | Replaces all routing rules |
active | boolean | — | Enable or disable the link |
clicksLimit | number | — | New click limit. Pass null to remove the limit |
expirationDate | string | — | New expiration datetime. Pass null to remove expiration |
tagIds | string[] | — | Replaces the tag list |
Minimal example (disable a link)
curl -X PUT https://api.revolink.link/apiv1/link/update \
-H "Content-Type: application/json" \
-H "X-Api-Key: rvlnk_your_api_key_here" \
-d '{
"workspaceId": "664abc000000000000000001",
"linkId": "664abc123def456789000001",
"active": false
}'
Full example (update all fields)
curl -X PUT https://api.revolink.link/apiv1/link/update \
-H "Content-Type: application/json" \
-H "X-Api-Key: rvlnk_your_api_key_here" \
-d '{
"workspaceId": "664abc000000000000000001",
"linkId": "664abc123def456789000001",
"name": "Summer campaign (updated)",
"description": "Updated description",
"active": true,
"clicksLimit": 50000,
"expirationDate": "2025-12-31T23:59:59Z",
"tagIds": ["664abc000000000000000012"],
"rules": [
{
"targetUrl": "https://example.com/ua-mobile-v2",
"isDefault": false,
"country": ["UA", "PL"],
"device": ["mobile"]
},
{
"targetUrl": "https://example.com/v2",
"isDefault": true
}
]
}'
Remove limits example
curl -X PUT https://api.revolink.link/apiv1/link/update \
-H "Content-Type: application/json" \
-H "X-Api-Key: rvlnk_your_api_key_here" \
-d '{
"workspaceId": "664abc000000000000000001",
"linkId": "664abc123def456789000001",
"clicksLimit": null,
"expirationDate": null
}'
Response 200 OK
{
"message": "Link updated.",
"id": "664abc123def456789000001"
}
Error Responses
| Status | Body | Cause |
|---|---|---|
400 | {"error": "Invalid linkId format."} | linkId is not a valid ObjectId |
403 | {"error": "You do not have permission to edit this link."} | Link belongs to a different workspace |
403 | {"licenseLimitReached": true, ...} | Rules limit reached on current plan |
404 | {"error": "Link not found."} | No link with this ID exists |
Delete a link
DELETE /link/delete
Request
DELETE https://api.revolink.link/apiv1/link/delete
Content-Type: application/json
X-Api-Key: rvlnk_your_api_key_here
Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
workspaceId | string | ✅ | Workspace ObjectId |
linkId | string | ✅ | ObjectId of the link to delete |
Example
curl -X DELETE https://api.revolink.link/apiv1/link/delete \
-H "Content-Type: application/json" \
-H "X-Api-Key: rvlnk_your_api_key_here" \
-d '{
"workspaceId": "664abc000000000000000001",
"linkId": "664abc123def456789000001"
}'
Response 200 OK
{
"message": "Link deleted."
}
Error Responses
| Status | Body | Cause |
|---|---|---|
400 | {"error": "Invalid linkId format."} | linkId is not a valid ObjectId |
403 | {"error": "You do not have permission to delete this link."} | Link belongs to a different workspace |
404 | {"error": "Link not found."} | No link with this ID exists |
Bulk create links
POST /links/bulk-create Up to 100 links per call. Each link is processed independently — if one fails, the rest still proceed.
Request
POST https://api.revolink.link/apiv1/links/bulk-create
Content-Type: application/json
X-Api-Key: rvlnk_your_api_key_here
Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
workspaceId | string | ✅ | Workspace ObjectId |
links | LinkInput[] | ✅ | Array of link objects. Maximum 100 items |
Each item in links accepts the same fields as /link/create, except workspaceId (provided once at the top level).
Example
curl -X POST https://api.revolink.link/apiv1/links/bulk-create \
-H "Content-Type: application/json" \
-H "X-Api-Key: rvlnk_your_api_key_here" \
-d '{
"workspaceId": "664abc000000000000000001",
"links": [
{
"name": "Campaign — Ukraine mobile",
"slug": "ua-mob",
"description": "UA mobile audience",
"active": true,
"clicksLimit": 5000,
"expirationDate": "2025-12-31T23:59:59Z",
"tagIds": ["664abc000000000000000010"],
"rules": [
{
"targetUrl": "https://example.com/ua",
"isDefault": false,
"country": ["UA"],
"device": ["mobile"]
},
{
"targetUrl": "https://example.com",
"isDefault": true
}
]
},
{
"name": "Campaign — Germany",
"rules": [
{
"targetUrl": "https://example.com/de",
"isDefault": false,
"country": ["DE", "AT", "CH"]
},
{
"targetUrl": "https://example.com",
"isDefault": true
}
]
},
{
"name": "Simple redirect",
"rules": [
{
"targetUrl": "https://example.com/simple",
"isDefault": true
}
]
}
]
}'
Response 200 OK
The response always returns 200 even if some items failed. Check the failed array for per-item errors.
{
"message": "Bulk create complete: 3 created, 0 failed.",
"created": [
{
"id": "664abc123def456789000001",
"slug": "ua-mob",
"fullLink": "https://rvlnk.link/ua-mob",
"name": "Campaign — Ukraine mobile"
},
{
"id": "664abc123def456789000002",
"slug": "xk7r2p",
"fullLink": "https://rvlnk.link/xk7r2p",
"name": "Campaign — Germany"
},
{
"id": "664abc123def456789000003",
"slug": "m9qw1z",
"fullLink": "https://rvlnk.link/m9qw1z",
"name": "Simple redirect"
}
],
"failed": []
}
Partial failure example:
{
"message": "Bulk create complete: 2 created, 1 failed.",
"created": [
{
"id": "664abc123def456789000001",
"slug": "ua-mob",
"fullLink": "https://rvlnk.link/ua-mob",
"name": "Campaign — Ukraine mobile"
}
],
"failed": [
{
"input": {
"description": "Missing name and rules"
},
"error": "Each link must have name and rules."
}
]
}
Error Responses
| Status | Body | Cause |
|---|---|---|
400 | {"error": "links must be a non-empty array."} | links is missing or empty |
400 | {"error": "Maximum 100 links per request."} | More than 100 items provided |
403 | {"licenseLimitReached": true, ...} | Plan quota would be exceeded |