Skip to main content

Automate Firebase Dynamic Links migration

Some teams created Firebase links via backend jobs. This guide shows how to recreate those slugs with the ULink REST API.


1. Generate an API key

Inside the dashboard open Project → API Keys → Generate Key. The key maps to a single project ID and is sent via the X-App-Key header on every /sdk/* request.


2. Recreate links with POST /sdk/links

Creates a new dynamic or unified link.

Headers

HeaderRequiredDescription
Content-TypeYesapplication/json
X-App-KeyYesYour project API key

Body

FieldTypeRequiredDescription
type"dynamic" | "unified"YesLink type
slugstringNoCustom slug (auto-generated if omitted)
namestringNoHuman-readable name for the link (shown in the dashboard)
domainstringNoVerified domain host. Required when the project has more than one verified domain.
iosFallbackUrlstringNoiOS App Store fallback (dynamic links)
androidFallbackUrlstringNoGoogle Play fallback (dynamic links)
iosUrlstringNoiOS destination URL (unified links)
androidUrlstringNoAndroid destination URL (unified links)
fallbackUrlstringNoGeneral web fallback URL
parametersobjectNoArbitrary key-value pairs passed to the app on open
metadataobjectNoCustom metadata including social media tags (see below)

Social media tags — The metadata object supports Open Graph fields that control how your link appears when shared on social platforms:

KeyDescriptionRecommended
ogTitleTitle shown in social previewsUp to 60 characters
ogDescriptionDescription shown in social previewsUp to 155 characters
ogImageImage URL shown in social previews1200x630px, HTTPS URL

ULink automatically renders these as <meta property="og:*"> and Twitter Card tags when social media crawlers fetch the link.

Example — dynamic link

curl -X POST "https://api.ulink.ly/sdk/links" \
-H "Content-Type: application/json" \
-H "X-App-Key: YOUR_ULINK_API_KEY" \
-d '{
"type": "dynamic",
"slug": "summer-offer",
"iosFallbackUrl": "https://apps.apple.com/app/id000000000",
"androidFallbackUrl": "https://play.google.com/store/apps/details?id=com.example.app",
"fallbackUrl": "https://example.com/campaign",
"parameters": {
"utm_source": "email",
"utm_campaign": "sunshine"
},
"metadata": {
"owner": "growth-team",
"ogTitle": "Summer Offer — 30% Off",
"ogDescription": "Grab our limited-time summer deal before it expires!",
"ogImage": "https://example.com/images/summer-offer.jpg"
},
"domain": "links.shared.ly"
}'

Notes

  • domain must be one of the verified hosts returned by GET /projects/:id/domains. If multiple domains exist and you omit it, the API returns an error.
  • Use type: "unified" with iosUrl / androidUrl instead of fallback URLs.
  • If no ogTitle is provided, ULink falls back to the link's name for social previews.

Returns the full link object for a given slug. This endpoint is public and does not require authentication.

curl "https://api.ulink.ly/sdk/links/summer-offer"

4. Attach installations & sessions

ULink tracks MAU via /sdk/bootstrap, /sdk/installations/track, and /sdk/sessions/*. This telemetry is required so we can count monthly active users and enforce free-tier usage limits. If you previously forwarded Firebase analytics events, move them to these endpoints.

POST /sdk/bootstrap

Combines installation registration and session creation in a single call.

Headers

HeaderRequiredDescription
Content-TypeYesapplication/json
X-App-KeyYesYour project API key

Body

FieldTypeRequiredDescription
installationIdstringNoUnique device identifier (generated server-side if omitted)
persistentDeviceIdstringNoStable ID for reinstall detection
deviceModelstringNoe.g. "iPhone16,2"
deviceManufacturerstringNoe.g. "Apple"
osNamestringNoe.g. "iOS"
osVersionstringNoe.g. "18.0"
appVersionstringNoe.g. "3.4.1"
appBuildstringNoBuild number
languagestringNoe.g. "en-US"
timezonestringNoe.g. "America/New_York"
metadataobjectNoCustom metadata

Example

curl -X POST "https://api.ulink.ly/sdk/bootstrap" \
-H "Content-Type: application/json" \
-H "X-App-Key: YOUR_ULINK_API_KEY" \
-d '{
"installationId": "device-123",
"deviceModel": "iPhone16,2",
"osName": "iOS",
"osVersion": "18.0",
"appVersion": "3.4.1",
"metadata": {
"source": "ios-app"
}
}'

Response

FieldTypeDescription
successbooleanWhether the call succeeded
installationIdstringPersisted device identifier
installationTokenstringSigned JWT you can attach to future resolve requests
sessionIdstring | nullCurrent session ID (null on free tier)
sessionCreatedbooleanWhether a new session was created
isReinstallbooleantrue if a previous installation was detected for this device

POST /sdk/installations/track

Registers or updates an installation without starting a session.

Headers: same as bootstrap.

Body

FieldTypeRequiredDescription
installationIdstringYesUnique device identifier
deviceModelstringNoDevice model
deviceManufacturerstringNoManufacturer
osNamestringNoOS name
osVersionstringNoOS version
appVersionstringNoApp version
appBuildstringNoBuild number
languagestringNoLocale
timezonestringNoTimezone
metadataobjectNoCustom metadata

Response

FieldTypeDescription
successbooleanWhether the call succeeded
installationIdstringPersisted device identifier
isNewbooleanWhether this is a first-time installation
installationTokenstringSigned JWT

POST /sdk/sessions/start

Starts a new session for an existing installation.

Body

FieldTypeRequiredDescription
installationIdstringYesInstallation to associate the session with
networkTypestringNoe.g. "wifi", "cellular"
deviceOrientationstringNoe.g. "portrait"
metadataobjectNoCustom metadata

Response

FieldTypeDescription
successbooleanWhether the call succeeded
sessionIdstringUUID of the created session

POST /sdk/sessions/:sessionId/end

Ends an active session. The SDKs call this automatically, but backend automation can also use it.

curl -X POST "https://api.ulink.ly/sdk/sessions/{sessionId}/end" \
-H "X-App-Key: YOUR_ULINK_API_KEY"

Response

FieldTypeDescription
successbooleanWhether the session was ended

Resolves a full URL or deep link and tracks the click event. Use this to validate any migrated slug. The endpoint enforces domain/project matching plus usage limits.

Query parameters

ParamRequiredDescription
urlYesFull URL to resolve, e.g. https://links.shared.ly/example

Optional headers

HeaderDescription
x-installation-tokenJWT from a previous bootstrap/track call
x-installation-idInstallation ID to attach
x-ulink-clientClient identifier, e.g. "migration-script"

Example

curl "https://api.ulink.ly/sdk/resolve?url=https://links.shared.ly/example" \
-H "User-Agent: cli-test" \
-H "X-ULink-Client: migration-script" \
-i

Inspect the JSON payload to ensure per-platform destinations and metadata match your expectations before flipping DNS or campaign URLs.


6. Deferred deep link matching — POST /sdk/deferred/match

Matches a deferred deep link by correlating a click fingerprint with an installation. This is used for attribution when a user clicks a link before the app is installed. Available on paid plans only.

Headers

HeaderRequiredDescription
X-App-KeyYesYour project API key

Body

FieldTypeRequiredDescription
fingerprintobjectYesDevice fingerprint data
clickIdstringNoClick ID to match against
installationIdstringNoInstallation ID

7. Suggested migration script

  1. Export Firebase link inventory to CSV/JSON.
  2. Map each record to the POST /sdk/links payload (choose dynamic vs unified, set verified domain, copy metadata/UTMs).
  3. Call /sdk/links for each entry; retry on rate-limit errors with exponential backoff.
  4. Optionally bootstrap installations by replaying device data via /sdk/installations/track or /sdk/bootstrap if you stored it historically.
  5. Use /sdk/resolve to smoke-test a sample of the imported slugs.

With these APIs wired up, all automation previously built on Firebase Dynamic Links will point entirely at ULink's backend.