API reference
com.optomatica:optolink-android exposes one entry point, OptoLink, plus its config, data, and error types, all under com.optomatica.optolink (errors under com.optomatica.optolink.error). Setup and initialization live in install & initialize; failure behavior in errors & retries.
The surface is Kotlin-first: suspend functions and Flow. There is no builder, no listener API, and no Java async variants.
// ---------- entry point ----------
class OptoLink private constructor(context, config) {
companion object {
/** Idempotent, concurrency-safe; awaits the once-per-install deferred match.
* Network failure during match: logged, NOT thrown
* (never blocks startup; re-attempt via resolveDeferredLink). */
suspend fun initialize(context: Context, config: OptoLinkConfig): OptoLink
fun instance(): OptoLink // IllegalStateException if not initialized
}
val deviceId: String // UUID v4, persisted
val config: OptoLinkConfig
// ---------- links ----------
/** SharedFlow(replay = 1): the deferred link replays to the first collector.
* Never errors — failures log via config.logger. */
val links: Flow<OptoLinkData>
/** Forward from Activity.onCreate AND onNewIntent.
* Safe to call before initialize completes — the latest intent is
* parked and processed once initialization finishes. */
fun handleIntent(intent: Intent)
/** Manual deferred re-attempt. null = no match;
* throws on network failure. */
suspend fun resolveDeferredLink(): OptoLinkData?
// ---------- identity & events (throw on failure) ----------
suspend fun setIdentity(externalId: String)
suspend fun clearIdentity()
suspend fun trackEvent(name: String,
properties: Map<String, String> = emptyMap())
fun dispose()
}
// ---------- data ----------
data class OptoLinkData(
val linkId: String?, // null for raw direct links
val path: String,
val params: Map<String, String>?, // verified flat string→string on the wire
val matchMethod: MatchMethod,
val confidence: MatchConfidence,
val isDeferred: Boolean,
val score: Int? = null, // backend matchScore: present only on FINGERPRINT_SCORED
)
enum class MatchMethod { CLIPBOARD, INSTALL_REFERRER,
FINGERPRINT_EXACT, FINGERPRINT_SCORED, IP_FUZZY,
DIRECT, NONE }
enum class MatchConfidence { EXACT, HIGH, MEDIUM, LOW }
// ---------- errors (two-class model, JVM naming) ----------
sealed class OptoLinkException(message: String) : Exception(message)
class OptoLinkApiException(
val status: Int,
val requestId: String?, // present only when the caller sent x-request-id
val details: List<OptoLinkErrorDetail>?, // 400 validation only
val retryAfterSeconds: Long?, // regexed from the 429 message
val raw: String?, // full body text verbatim
) : OptoLinkException(...)
class OptoLinkConnectionException(cause: IOException) : OptoLinkException(...)
data class OptoLinkErrorDetail(val message: String, val field: String?)
// ---------- config ----------
data class OptoLinkConfig(
val apiKey: String, // CLIENT-tier opl_sdk_… (no env fallback)
val orgKey: String,
val baseUrl: String,
val timeout: Duration = 10.seconds, // per HTTP attempt
val maxRetries: Int = 2,
val clipboardEnabled: Boolean = true, // opt-out of the deferred match's clipboard leg
val logger: OptoLinkLogger? = null, // @JvmOverloads on the constructor
)
fun interface OptoLinkLogger {
fun log(level: OptoLinkLogLevel, message: String, error: Throwable?)
}
enum class OptoLinkLogLevel { INFO, WARNING, ERROR }
What emits on links
| Source | matchMethod | isDeferred |
|---|---|---|
| Verified App Link open, resolution succeeded | DIRECT | false |
| Custom-scheme or non-matching-org open, or backend unreachable at resolve time | DIRECT (linkId = null) | false |
| First-launch clipboard match | CLIPBOARD | true |
| First-launch install-referrer match | INSTALL_REFERRER | true |
| First-launch fingerprint match | FINGERPRINT_EXACT / FINGERPRINT_SCORED | true |
| First-launch IP match | IP_FUZZY | true |
confidence is backend-reported and applies to deferred matches; direct opens carry EXACT. params is flat string → string and null when the link has none. score carries the backend's numeric match score on FINGERPRINT_SCORED matches only; every other method leaves it null.
Release history: changelog.