Skip to content

Documentation

When it does not work

Every failure state, what it means, and what to do about it. Including the Apple caching delay that makes a correct setup look broken for up to 24 hours.

Start here: Apple caches your association file for up to 24 hours

This is the commonest reason a correct setup appears broken, and if you read nothing else on this page, read this.

iOS does not fetch your association file from us. It fetches it from Apple’s CDN, which fetches it from us on its own schedule and then caches it — for up to 24 hours. So if you configure everything correctly right now and test in two minutes, iOS may still be holding the version from before you configured anything, which claimed nothing, and your link will open Safari.

Nothing is wrong. Nothing needs changing. There is a flag for this:

Associated Domains, while you are developingtext
applinks:YOUR-KEY.links.quberoute.com?mode=developer

?mode=developer tells iOS to fetch the file directly from us every time, bypassing Apple’s cache entirely. It works on a device with Developer Mode enabled (Settings → Privacy & Security → Developer Mode) and it makes the loop seconds instead of hours.

Remove it before you ship. With it in place, every launch of your released app fetches the file from us directly, which is slower for your users and pointless once the configuration has settled.

After removing it, expect up to 24 hours before Apple’s cache catches up. Plan the release accordingly, and do not interpret the gap as a fault.

The link opens Safari instead of the app

In the order these are actually the cause:

  1. Apple’s cache. See above. Add ?mode=developer and try again.
  2. You typed the link into Safari’s address bar. iOS deliberately does not open apps for links typed into the address bar. Put the link in a note, a message or an email and tap it from there.
  3. Associated Domains is not on the App ID. Check your Apple Developer account, then regenerate the provisioning profile. Xcode with automatic signing usually handles this; when it does not, nothing else will work until it does.
  4. The host does not match. Live and test are different hosts — YOUR-KEY and YOUR-KEY-test. A test link with only the live domain declared will not open the app.
  5. You are inside an in-app browser. Instagram, Facebook, Gmail and others often keep the link inside their own browser rather than handing it to iOS. Nothing is misconfigured and there is no setting that changes it — it is the embedding app’s decision. Your web fallback is what the person sees.
  6. The app was installed from Xcode over a previous build. Delete the app from the device and install again; iOS caches the entitlement.

Before you work through that list, check our own file is right: the app settings page has a validator that fetches your association file, checks every one of Apple’s requirements against the real response, and can also ask Apple’s CDN what it is currently serving. If ours is correct and Apple’s is stale, that tells you it is the cache and not you.

The app opens, but onLink never fires

  • @capacitor/app is not installed. That plugin is how a launch URL reaches the SDK at all; without it, links open your app and nothing else happens. Run npm install @capacitor/app && npx cap sync and rebuild. The SDK reports this through onError on the first launch, so pass one while you are integrating — see below.
  • You registered onLink too late. The SDK holds a link that arrives before your handler exists and delivers it the moment you register — but if you register it after an await on something slow, your user has already seen the wrong screen. Register it immediately after init.
  • The alias does not exist in this environment. A test key resolving a live link gets wrong_environment, with a message saying so.
  • Your handler threw. The SDK catches exceptions from your handler so your bug does not become our crash — but that means a broken router looks like nothing happening. Pass onError to init while you are developing.
See everything the SDK is unhappy abouttypescript
const qr = await QubeRoute.init({
  key: 'qr_test_YOUR_KEY',
  onError: error => console.error('[QubeRoute]', error),
})

What the server tells you, and what each answer means

Error codes returned by the SDK endpoints and what to do about each
What comes backWhat it means
wrong_key_kindYou have shipped a secret key inside your app. Create a publishable one and revoke this one — if the build was released, anybody who downloaded it has it.
unauthorisedThe key is not one we recognise, or it has been revoked. Copy it again from the SDK setup page.
wrong_environmentA test key was asked about a live link, or the reverse. Use the key that matches the environment you are testing.
no_such_linkThe alias does not exist in this app and environment. Usually a link created in the other environment.
not_your_linkThe address belongs to a different app. Check the link host in your Associated Domains entry.
rate_limitedToo many requests from this network address. It is per address rather than per app, and it resets shortly. Common when reinstalling repeatedly while testing.
temporarily_unavailableOurs, not yours. Events are kept and retried; a deferred match is not retried, because an install is only ever matched once.

A deferred install was not attributed

Often correct rather than broken. The match endpoint tells you which kind of “no” it was:

  • no_candidates — no click in the window that could be this device. Usually the person tapped on wifi and opened the app on cellular, which changes the network address.
  • below_threshold — something was close, but not close enough to assert. We do not report guesses as installs.
  • ambiguous — two or more different links fitted equally well, so we attributed none. Common on a shared network.
  • disabled_by_app — probabilistic matching is switched off for this app, in its settings.

The expected rates are on the deferred matching page, including the case where the method does badly.

Events are not appearing

  • Events are queued when the device is offline and sent with the next one. The queue holds a hundred; beyond that the oldest are dropped.
  • An event with no name is dropped, and the response says how many were.
  • A value needs a currency. { value: 4.99 } with no currency records the event without the money.
  • Test and live are separate. An event from a test key never appears in a live report.

Still stuck? Write to [email protected] with your app key and roughly when you tested, and we will look at what our server actually saw.

Ask the documentation

It answers from these pages only, and links what it used. If the answer is not here it says so rather than guessing — then email [email protected].

← All documentation