WarpLink
Trust and reliability

Fallbacks When the API Is Unreachable

What the WarpLink SDKs do when the API cannot be reached, plus a fallback pattern with code for iOS, Android, React Native, and Flutter.

The SDK needs the API to turn a tapped link into a destination. When the API cannot be reached, the tap still opens your app, because the operating system opens it from the Universal Link or App Link. What is missing is the destination. The SDK reports an error, and your app decides what to show. This page describes what the SDK does and gives a fallback pattern you can copy.

Behavior below is for SDK 1.1.0.

What the SDK does

StepBehavior
AttemptsA link resolve or a deferred check is tried up to 3 times.
Time limitsThe first attempt has a 4 second limit. Each retry has a 3 second limit.
WaitsThe SDK waits about 0.5 seconds before the second attempt and about 1 second before the third, plus up to 0.15 seconds of random jitter.
Retry budgetBefore each retry the SDK checks the time spent since the first attempt, and starts no retry once 12 seconds have passed. This is a budget checked between attempts, not a hard deadline. Every attempt also has its own time limit.
What is retriedDead or flaky connections, timeouts, DNS failures, and 5xx responses.
What is not retriedRefusals. A missing link, a password-protected link, an expired link, a rejected key, and any other 4xx are answered on the first attempt. The 1.1.0 SDKs do not retry a 429 either.
Duplicate tapsA failed resolve releases the duplicate-tap guard, so tapping the same link again resolves it again.
DeliveryAfter the last attempt fails, the error goes to your onLink callback. Your app keeps running.
Deferred checkA deferred check that fails on first launch is not marked complete, so the next launch tries again. It reaches your callback as an error too, and it is a report, not an instruction to act.

With the limits above, the worst case is about 11.8 seconds: 4 + 0.5 + 3 + 1 + 3 seconds of attempts and waits, plus up to 0.3 seconds of jitter. The figures match the SDK source for iOS and Android. React Native and Flutter use the native SDKs underneath, so they behave the same.

A custom link domain that you declare in linkDomains is recognized with no network on the first launch. Domains the server returns from GET /v1/sdk/validate are cached after the first successful call.

Why not retry in a loop

The SDK has already retried within its budget. A tight loop of your own adds load to a service that is probably struggling and keeps the user waiting. Show your default screen first, then let a manual action or the next launch try again.

Build the fallback

Two kinds of failure reach your callback, and they need different handling.

  • A resolve failure. The user just tapped a link, so they expect to land somewhere. Fall back as described below.
  • A deferred-check failure. This is the background attribution check on first launch. Nobody tapped anything in the app, so do nothing visible. Never navigate away from the screen the user is on because of a late attribution error.

The samples tell them apart by recording the tapped URL, with the time, when the operating system delivers it. An error counts as a resolve failure only if a link was tapped in the last 30 seconds. Otherwise the sample does nothing.

For a resolve failure, use three layers in this order.

  1. Read the URL yourself. The URL has the form https://<host>/<slug>?<query>. The path is one segment, the slug, and it names no screen. The screen lives in the link's configuration on our servers. What you can read offline is the query string. If you add your own parameters to the links you share, such as ?screen=pricing, route on them.
  2. Use a cached route. After every successful resolve, store the route. A link is unique by host and slug, so the same slug on two domains can be two different links. Key the cache by host, path, and query string, so that query values that change routing get their own entry. Store a timestamp with each route and ignore entries older than 30 days, because an edited link can leave a cached route out of date.
  3. Open the default screen. If neither layer yields a route, open the screen the app opens on its own. A failed resolve must never show a blocking error.

Do not treat a fallback route as an attribution result. A route recovered from your cache or from the URL says nothing about who installed the app or where they came from.

A successful resolve clears the pending tapped URL, so a later deferred-check failure can never navigate. In each sample, router stands for your own navigation code.

iOS (Swift)

Android (Kotlin)

React Native

AsyncStorage is one choice of storage. Any key-value storage works.

Flutter

In automatic mode the Flutter plugin does not hand the tapped URL to your callback. Use the URL your own router or link listener receives, and resolve it with WarpLink.handleDeepLink, which throws a typed exception on failure. Because you call it for a link the user tapped, every failure here is a resolve failure. A deferred-check failure arrives as an ErrorEvent in onLink, and needs no navigation.

When you resolve links yourself this way, configure the plugin with automaticDeepLinks: false, so that one code path owns navigation. See the Flutter SDK for the full setup.

Test it

  1. Put the device in airplane mode.
  2. Tap a link that was resolved earlier, and confirm the app opens on the cached route.
  3. Tap a link that was never resolved, and confirm the app opens on the default screen.
  4. Turn airplane mode off, tap the same link again, and confirm it resolves.

What this does not cover

  • Attribution on first launch. If the API is down at first launch, the deferred check fails and tries again on the next launch. A user who opens the app only once in that window is not attributed.
  • Custom parameters. Parameters that live on the link, not on the URL, are not available offline unless you cached them.
  • Password-protected links. The SDK returns a password-required error. Open the short URL in a browser, as described in the SDK error handling guide.

For what keeps working on our side during an outage, see Architecture and failure modes.

On this page