Skip to content
CodeOnAir
← All posts

How to make a QR code that opens your app (or the right store)

One QR code for posters, packaging and counters: it opens your app on the right screen if it's installed, sends everyone else to the App Store or Play Store, and keeps the offer through the install.

CodeOnAir team27 October 20269 min read

A QR code on a poster, a product box or a shop counter usually has one job: get the person into your app. Most "app download QR code" generators do half of that. They detect iPhone or Android and send people to the right store. They don't open the app for people who already have it, and they can't take a new user to the offer they scanned for once the app is installed.

This guide makes one QR code that does all three, using an ordinary https:// link that your app is verified to open.

What should happen when someone scans the code

Every scan ends in one of three situations. Decide what each should do before you print anything.

Who scans What should happen
The app is installed The app opens on the screen the code is about: the offer, the product, the table number.
The app isn't installed The right store opens. After the install, the first launch still lands on that screen.
Someone opens the link on a computer Your website, or a page with the QR code so they can scan it with their phone.

The third case matters more than it sounds. People type short links from posters into a laptop, and the link gets forwarded in emails.

Why a plain store link or a custom scheme QR code fails

A store link (https://play.google.com/store/apps/details?id=…) only works on one platform. iPhone users who scan a Play Store link get a web page they can't use. Even with one code per store, people who already have the app go to the store listing instead of the screen you meant.

A custom scheme (myapp://offer/diwali) opens the app only if it's installed. If it isn't, nothing useful happens: there's no fallback to the store, and nothing remembers the offer through the install. Scanner apps also handle custom schemes differently.

An https:// link on a domain your app is verified for avoids both problems. It's a Universal Link on iOS and an App Link on Android, so the system can hand it straight to your app. When the app isn't there, the same URL loads a server that sends the person to the right store and remembers what they scanned.

How phones handle a scanned https link

Scanning a QR code doesn't open anything by itself. The phone shows the link and the user taps it, much like tapping a link in a message.

  • iPhone: the Camera app and the Code Scanner show the URL in a banner. Tapping it opens the link. If the domain is set up for Universal Links and the path matches, iOS can open your app directly; otherwise Safari opens.
  • Android: the camera app or Google Lens shows the link. Tapping it sends the URL to the system. If your App Links are verified for the domain, the app can open directly; otherwise a browser opens.

Exactly what happens varies by phone maker, OS version and scanner app, so test on the phones your users have. What we can say precisely is what the link server does when the scan lands in a browser instead of the app. With CodeOnAir:

  • iPhone, no app (or the browser opened): a short page sends the person to your App Store listing.
  • Android, no app (or the browser opened): a redirect to the Play Store with a referrer that carries the click ID.
  • Scanned inside the Google app or another in-app browser: the server sees the in-app browser in the user agent and shows a page with Open in app and Get the app buttons, because in-app browsers usually don't hand links to apps. Our post on links that open in Instagram's browser explains why.
  • A computer: the link's desktop URL or your app's website URL. With neither set, a landing page shows the link's QR code.

If verification isn't working on your domain, start with our apple-app-site-association and assetlinks.json setup guide, and run your domain through the free Universal Link and App Link Validator.

Static vs dynamic QR codes: why the link behind the code should be editable

A QR code is only an encoded string. If that string is the final destination, the code is "static": change your store URL or campaign and you reprint.

If the string is a short link (https://yourapp.codeonair.com/diwali), the code never changes, but what the link does can. You can change the in-app path, the parameters, the fallbacks and the preview later, and every printed code follows.

Two rules follow from that:

  • Never delete or disable a link that's in print. A disabled link shows "Link not found", and the paper stays in shop windows for years.
  • Keep the URL short. A short URL needs fewer modules (the black and white squares), so the code can be printed smaller and still scan.

Step by step: create the link, set fallbacks, download the QR

In the CodeOnAir dashboard, open AppLinks and create a link:

  1. Short link: pick a readable slug, such as diwali or store-pune-01.
  2. In-app path and parameters: where the app should go, for example path /offer with code=DIWALI24.
  3. iOS and Android behaviour: leave both on Open the deep link URL in your app. If the app isn't installed, people go to the App Store or Google Play. You can set a custom URL instead, but on Android a custom URL replaces the Play Store link, so the install referrer isn't added.
  4. Desktop / web URL: where a computer should go. Empty means your app's website URL; with no website URL either, computers see a landing page with the QR code.
  5. Social tags: the title, description and image shown when the link is shared.
  6. Campaign: a name such as diwali-posters, so you can add up results later.

Then open the link, go to Share & QR, and use Download SVG for print or Download PNG (1024 px) for screens and slides.

You can do the same from your backend with the REST API, which suits one code per shop or per table:

curl -X POST https://app.codeonair.com/v1/links \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "store-pune-01",
    "path": "/offer",
    "params": { "code": "DIWALI24", "store": "pune-01" },
    "campaign": "diwali-posters",
    "og_title": "Diwali offer: 20% off your first order"
  }'

# The QR code for the link id in the response
curl -H "Authorization: Bearer sk_live_…" \
  "https://app.codeonair.com/v1/links/lnk_…/qr?format=svg&margin=4" -o store-pune-01.svg

The QR endpoint returns SVG or PNG. size is in pixels (64 to 2,048, default 512), margin is the quiet zone in modules (0 to 10, default 2), and dark and light take six-digit hex colours. The code uses error correction level M.

The API key (sk_live_…) belongs on your server only. During a new Free workspace's first seven days, links that go to a website show a one-tap "This link goes to…" page first; links that open the app or a store aren't affected.

Keep the screen after install (deferred deep linking)

The scan-to-store-to-app path is where most QR campaigns lose people. Someone scans a "₹100 off your first order" poster, installs the app, opens it, and sees the home screen with no offer. That's the gap deferred deep linking closes.

With the React Native SDK installed, the first launch after the install receives the link's path and parameters, flagged as deferred:

import DeepLink from "@codeonair/deeplink-react-native";

DeepLink.init();
DeepLink.onLink((link) => {
  // link.path "/offer", link.params { code: "DIWALI24", store: "pune-01" }
  navigation.navigate("Offer", { code: link.params.code, fromStore: link.params.store });
});

How the install is matched to the scan:

  • Android: the Play Store hands the click ID back to the app through the install referrer, so the match is exact. Add react-native-play-install-referrer to the app so the SDK can read it.
  • iOS: there's no referrer, so the first open is matched to the scan on signals such as the network address, OS version, language and time. Our post on deferred deep linking on iOS without the IDFA explains how and where it fails.

Two things matter for posters in particular. First, the attribution window: how long after a scan an install still counts. Someone may scan in a shop and install that evening. Each link can set its own window: up to 1 hour on Free, 24 hours on Starter and Growth, and 7 days on Scale. Second, networks: on iOS, a person who scans on shop Wi-Fi and installs on mobile data may not be matched. Opt-in clipboard matching makes iOS matches exact at the cost of a paste prompt.

Print checklist: size, quiet zone, contrast, testing

  • Size for the distance. A common rule of thumb is a code about a tenth as wide as the scanning distance: around 3 cm for a counter card read from 30 cm, much larger for a poster across a room. Test at the real distance.
  • Quiet zone. The QR standard asks for a blank margin four modules wide. The default download has a margin of two, so leave white space around the code in your design, or request margin=4 from the API.
  • Contrast. Dark code on a light background. Inverted (light on dark) codes and low-contrast brand colours fail on some scanners.
  • Logos. Level M recovers from roughly 15% damage, which isn't much room. Keep logos small, or off the code.
  • Use the SVG for print. It stays sharp at any size. PNG is for screens.
  • Test on at least three phones before the print run: an iPhone, a recent Android and an older budget Android. Test with the app installed, after uninstalling it, and from the Google app's scanner.
  • Print the short URL under the code for people whose camera won't scan.

Measuring scans: one link per poster, store or city

A scan is a click on the link, so you measure QR codes the same way as any other link. To compare places, give each poster, store or city its own link, and put them in one campaign:

Link Campaign
yourapp.codeonair.com/pune-fc-road diwali-posters
yourapp.codeonair.com/pune-baner diwali-posters
yourapp.codeonair.com/mumbai-andheri diwali-posters

Link analytics then shows clicks, installs and app opens per link and for the whole campaign, by platform and country, with CSV export. Analytics is on Starter and up. On Free, clicks are still recorded for install matching, but the stats aren't shown. The Free plan includes 100 app links; paid plans have no link limit.

FAQ

Can one QR code work for both iPhone and Android?

Yes. Encode one https:// link. The link server checks the platform of each scan and sends iPhones to the App Store and Android phones to Google Play, while phones with the app installed can open it directly.

Do I need to reprint if the link changes?

No, as long as the code points to a short link you can edit. Change the path, fallbacks or campaign behind it and printed codes follow. Just don't disable or delete the link.

Does a QR code open the app directly from the iPhone camera?

It can. The Camera shows the link and the user taps it. If your domain is set up for Universal Links and the path matches, iOS can open the app; otherwise Safari opens and the link sends the person on. Test on the iOS versions your users have.

Can I track QR code scans?

Yes. Each scan is a click on the link. Use one link per location and a shared campaign to compare them. Clicks, installs and opens per link are shown on Starter and up.

Is SVG or PNG better for print?

SVG. It's vector, so it stays sharp at any size and printers can scale it. Use PNG for screens, slides and messaging apps.

Create a link and download its QR code

Create a free account. The Free plan includes 100 app links on a yourapp.codeonair.com domain, with QR codes for every link. See AppLinks for what the links do, and pricing for analytics and longer attribution windows.


The link behaviour described here is what CodeOnAir's link server does. How a scanner app opens a link differs between phones and OS versions; test on your own devices before printing.

Your first AppLink in five minutes.

Create an app, pick a subdomain, and share a link that opens your app. Try Growth free for 30 days — no card needed.