All field notes

How to move from Firebase Dynamic Links to Ferry.

Firebase Dynamic Links is gone. Your replacement plan still needs to preserve the important parts: one link across platforms, the right screen after open or install, and attribution you can trust.

OLD ROUTE promo.page.link/summer Firebase Dynamic Links
NEW ROUTE https://go.acme.com/summer Ferry link, payload preserved
MOVED
25
AUG

The shutdown date has passed.

Google shut Firebase Dynamic Links down on August 25, 2025. Both custom-domain links and page.link links stopped being served. Google also says page.link domains cannot be transferred, and that link metadata was marked for deletion at shutdown.

Read Google’s deprecation FAQ

That changes the shape of a migration. If you exported your Dynamic Links metadata before shutdown, you can rebuild from that file. If you did not, this is a reconstruction project: recover the destinations and campaign intent from the places where your team used those links, then create durable replacements.

01

Start with the links that still matter.

Do not begin by recreating every historical short URL. Begin with the links that still have distribution or business value. Old QR codes, packaging, referral programs, lifecycle emails, app invites, and paid campaigns deserve attention before a launch announcement from three years ago.

If you have a Firebase export, normalize it into one working sheet with:

  • the old short URL and suffix;
  • the deep link or in-app destination;
  • iOS, Android, and desktop fallbacks;
  • campaign source, medium, and name;
  • where the link is still published;
  • an owner and migration status.

Without an export, search campaign tools, email templates, QR source files, backend records, app configuration, support macros, and analytics history. Put the recovered links into the same sheet. This inventory becomes the cutover checklist.

Move the links that still have a job, not the links that merely exist.

02

Map the old model to Ferry.

The concepts are close, but the payload should not be treated as a pile of redirect parameters. In Ferry, a project represents one app. A hostname belongs to one project, and each link carries structured data that your app reads.

Firebase Ferry Migration note
URL prefix Shared or custom hostname Use a new feryl.io hostname, or connect a hostname you own.
Deep link parameter payload.data and optional payload.url Route from structured data first. Treat the URL as optional.
iOS and Android app settings One Ferry project Bundle ID, package name, store URLs, and web fallback live together.
Dynamic Links SDK callback Resolve and match Direct opens resolve a link. First opens after install call match.
Link analytics Click, match, signup, and purchase attribution Carry the Ferry click ID into the product events that matter.

Use payload.data as the contract between a link and your app. A link to a summer offer might carry { "screen": "offer", "campaign": "summer" }. Your app owns the final navigation logic, so it can evolve without baking fragile routes into every campaign URL.

04

Replace the app integration in two paths.

Deep linking has two moments, and both need to be tested. The direct path opens an installed app. The deferred path sends a user to the store, then restores the link payload on first launch after install.

A

App already installed

Configure Universal Links on iOS and App Links on Android. Ferry serves the AASA and assetlinks.json files for the project’s hostname. When the app receives the URL, resolve it and route from the returned payload.

B

App installed after click

On the first launch, call POST /v1/match. Android can pass the Play Install Referrer. iOS can pass an App Clip or pasteboard click ID when available, with a coarse fingerprint as a probabilistic fallback.

Ferry returns a match method and confidence with the payload. Deterministic signals are preferred. If a fingerprint could refer to more than one click, Ferry returns no match instead of guessing.

FIRST OPEN RESPONSE 200 OK
{
  "matched": true,
  "method": "install_referrer",
  "confidence": { "kind": "deterministic" },
  "clickId": "clk_3Qm8xR2v",
  "payload": {
    "data": { "screen": "offer", "campaign": "summer" }
  }
}

Keep the returned clickId. Send it when the user signs up or makes a purchase so the dashboard can connect the outcome to the link and campaign, not just count the open.

05

Cut over only after the full journey passes.

Use a staging hostname or an unpublished slug while the app change is under review. Test real devices where possible. Browsers, in-app web views, stores, and operating systems do not all behave like a simulator.

An installed iOS app opens the intended screen from the link.

An installed Android app opens the intended screen from the link.

A device without the app reaches the correct App Store or Play Store page.

The first launch after install receives the expected payload.

A desktop visitor reaches the configured web fallback.

A signup or purchase appears against the right link and campaign.

For a custom domain, lower DNS TTL before the cutover if your provider allows it. Recreate the important slugs, verify the well-known files, ship the app update, and only then point the hostname at Ferry. Keep a rollback note until traffic and attribution look normal.

For old page.link URLs, there is no DNS switch to make. Replace them at every touchpoint you control, publish app and support messaging for the rest, and use campaign reports to find high-value sources that still send traffic to dead links.

ARRIVE

The goal is not link parity. It is journey continuity.

A successful migration leaves users with a working route, your app with a small and explicit payload contract, and your team with attribution from the first click to the outcome that matters.