Skip to content
Sendora Cloud
Create account
Grow

Deep Links

Branch / Firebase Dynamic Links parity: short links that survive the App Store / Play Store detour and reopen on the article, product, or playlist the user shared from.

Features

  • **SDK-side `sendora.links.create()`** — mint links from inside the app with a public key (pk_*); typed `linkData<T>` generic; bundle-id gated server-side.
  • **Deferred deep linking (iOS)** — canonical fingerprint match: IP-pinned + 2h window + atomic flip. SDK auto-computes the fingerprint; zero caller-side boilerplate. Cold-install attribution that's better than nothing.
  • **Custom JSON `linkData`** delivered to the app on warm + cold open — route via your own business ids (`articleId`, `productId`, ...).
  • **Auto-served AASA + assetlinks** per registered iOS / Android app — `/internal/aasa` + `/internal/assetlinks` regenerated on bundle registration.
  • **Typed errors** — `LinkError(code, statusCode)` with `BUNDLE_MISMATCH` / `PLAN_LIMIT` / `RATE_LIMITED` / `FALLBACK_REQUIRED` / ... — pattern-match instead of string-sniff.
  • **SDK-side `links.revoke()`** for private-content unsend + `links.getStats()` (no dashboard scraping).
  • **Click analytics** — geo, device, OS, referrer, deferred-match rate; `/clicks` + `/stats` + `/time-series` endpoints per link.
  • **Per-link Open Graph preview overrides** — `ogTitle` + `ogImageUrl` for richer share cards on iMessage / Slack / WhatsApp.
  • **Per-platform fallback URLs** — `fallbackUrl` optional; backend defaults from your apps registry (web origin > App Store URL > Play Store URL).
  • **Honest non-features:** no `links.prewarm()` background-mint cache (every create is a real HTTPS call); no Android Play Install Referrer integration (iOS fingerprint match is the only deferred path); no custom-branded domains + wildcard SSL (all links served from one canonical Sendora short domain).

Common use cases

  • Replace Branch / Firebase Dynamic Links + their CRM-sync plugin + a separate attribution SDK.
  • Share-button → article / product / playlist that opens the in-app screen + attributes the share recipient's install.
  • Referral + invite flows with deferred attribution AND the referrer auto-credited via audiences + workflow follow-up.
  • Email / SMS deep links to specific content with click-through attribution closed back into Analytics.

Key concepts

Shortcode
Auto-generated 7-char id (e.g. `aB3xK9p`) that resolves to `iosDeepLinkPath` / `androidDeepLinkPath` + `linkData`.
Bundle-id gate
Backend cross-checks the SDK's bundle / package against your registered apps. A leaked `pk_*` can't mint links pointed at someone else's app.
Deferred match
User taps a link, installs the app, opens it for the first time — SDK calls `matchDeferred()` and the backend returns the original `linkData` so you can route to the article they came from.
linkData
Typed JSON delivered to `onLinkOpened`. Pass a discriminated union for type-safe routing.

Setup

  1. 1
    Mint a public API key
    Dashboard → Settings → API Keys → `pk_prod_*`.
  2. 2
    Register your app
    Dashboard → Apps. iOS: bundle id + Team ID. Android: package + SHA-256 signing fingerprint (`./gradlew signingReport`).
  3. 3
    AASA + assetlinks auto-served
    `https://go.sendoracloud.com/.well-known/{apple-app-site-association,assetlinks.json}` populate within seconds.
  4. 4
    Wire Associated Domains / App Links
    iOS: add `applinks:go.sendoracloud.com` to Signing & Capabilities. Android: add intent-filter with `autoVerify="true"`.

Mint + share a link

Production gotchas

  • Deferred match has a 2h window from click. Mobile install latency rarely exceeds minutes; tighter window reduces brute-force surface.
  • Bundle-id gate is **fail-closed** — fresh projects with zero registered apps refuse `links.create()` from the SDK. Register your app before going to prod.
  • Android Play Install Referrer is 100% accurate; the iOS fingerprint fallback is probabilistic (~80–90%). Always wire the referrer path on Android.
  • Use typed `linkData<T>` with a discriminated union — `(linkData.articleId as string | undefined)` casts age badly.
  • `fallbackUrl` is optional from the SDK as of API 1.8.0 — backend defaults from your apps registry (web origin > iOS App Store URL > Android Play Store URL).

Related