Creating links
Make short links from inside the app for share and invite flows with Lander.createLink.
Lander.createLink creates a Lander short link from the app, for example when someone taps Share on a product or Invite a friend. The link works like one made in the dashboard: it opens your app on the right screen, or sends people without the app to the store or your website.
Create a link
private fun shareProduct(id: String, name: String) {
lifecycleScope.launch {
try {
val link = Lander.createLink(
path = "/product/$id",
params = mapOf("productId" to id),
title = name,
channel = "share",
)
val send = Intent(Intent.ACTION_SEND)
.setType("text/plain")
.putExtra(Intent.EXTRA_TEXT, link.url)
startActivity(Intent.createChooser(send, "Share"))
} catch (e: LanderException) {
Toast.makeText(this@ProductActivity, "Could not create a link: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}link.url is the shareable URL, for example https://acme.golander.link/x7Kp2Qa. createLink suspends while the request runs, off the main thread, so call it from a coroutine such as lifecycleScope.
When someone opens that link, your app receives a LanderLink with path == "/product/123" and params == mapOf("productId" to "123"). See Receiving links.
From Java
Build a LanderLinkOptions and pass a callback, called on the main thread:
LanderLinkOptions options = new LanderLinkOptions.Builder()
.path("/product/" + id)
.param("productId", id)
.title(name)
.channel("share")
.build();
Lander.createLink(options, new LanderCallback<LanderShortLink>() {
@Override
public void onSuccess(LanderShortLink link) {
share(link.getUrl());
}
@Override
public void onError(Exception error) {
// A LanderException for API and network failures.
}
});Kotlin can pass a LanderLinkOptions too: Lander.createLink(options).
Parameters
suspend fun createLink(
path: String = "/",
params: Map<String, Any> = emptyMap(),
title: String? = null,
campaign: String? = null,
channel: String? = null,
tags: List<String> = emptyList(),
iosFallbackUrl: String? = null,
androidFallbackUrl: String? = null,
webFallbackUrl: String? = null,
ogTitle: String? = null,
ogDescription: String? = null,
ogImageUrl: String? = null,
expiresAt: Date? = null,
): LanderShortLinkProp
Type
What you cannot choose from the app:
- The slug is always random (7 characters).
- The domain is the project's default domain.
- The environment follows the key: a
pk_test_key creates test links, apk_live_key creates live links.
Fallback URLs change deferred linking
A link with an Android fallback URL sends Android users without the app to that URL instead of the Play Store, so it cannot carry an install referrer token. Leave it empty if you want deferred deep links. See Deferred links.
The result
createLink returns a LanderShortLink:
| Field | Type | Example |
|---|---|---|
id | String | Link ID (UUID) |
url | String | https://acme.golander.link/x7Kp2Qa |
uri | Uri | url as an android.net.Uri |
domain | String | acme.golander.link |
slug | String | x7Kp2Qa |
path | String | /product/123 |
params | Map<String, Any> | {productId=123} |
title, campaign, channel | String? | As sent |
tags | List<String> | As sent |
expiresAt, createdAt | Date? | Parsed from the API |
toString() returns the URL, so "Check this out: $link" reads naturally.
Errors
createLink checks two things before it sends anything, and throws an IllegalArgumentException when:
pathdoes not start with/.- A
paramsvalue is not aString,NumberorBoolean(or is a non finite number).
It throws IllegalStateException before Lander.initialize. Everything else is checked by the API. Failures throw a LanderException:
code | statusCode | Meaning |
|---|---|---|
validation_error | 400 | A field is too long or malformed (see the limits above). |
destination_blocked | 400 | A fallback URL failed Lander's safety screening. |
rate_limited | 429 | Too many links from this install (20 per minute) or project (60 per minute). retryAfter says when to try again. |
link_quota_exceeded | 429 | The project created 1000 links in the last 24 hours. |
invalid_api_key | 401 | The publishable key is wrong or revoked. |
network_error, timeout | null | No response after retries. |
invalid_response | null or 2xx | The response was not the JSON Lander sends, for example a captive portal page. |
e.isRetryable is true for network errors, timeouts, invalid_response, 408, 429 (except link_quota_exceeded) and 5xx responses. e.retryAfter is a kotlin.time.Duration; Java reads getRetryAfterMillis().
Retries never create two links
The SDK retries network errors, timeouts, 408, 429 and 5xx responses up to 3 attempts in total, with a short backoff (and waits at least as long as a Retry-After header asks). It waits at most 10 seconds in total between attempts, so a share button never hangs on a long Retry-After: the LanderException comes back with retryAfter instead. link_quota_exceeded is not retried. Every attempt carries the same Idempotency-Key, and Lander replays the first successful response for a repeated key, so a retry after a lost response returns the same link instead of creating a second one.
The install ID is sent along, which is how the API applies the per install rate limit.