Skip to main content

Direct API Integration Guide

This guide provides complete implementation details for integrating directly with Ulinkly's API endpoints. If you're looking for a quick overview, see the REST API first.

Prerequisites
  • Ulinkly account and API key (get one here)
  • Understanding of HTTP requests and JSON
  • Secure storage mechanism for tokens (SharedPreferences, UserDefaults, etc.)

Integration Flow

Ulinkly requires two steps for proper tracking:

  1. Installation Tracking: Identify unique app installations
  2. Session Management: Track when users are active

Follow this sequence: Installation → Session → Normal API usage

Implementation Steps

1. Initial Setup

# Set your API key
export ULINK_API_KEY="your-api-key-here"
export ULINK_BASE_URL="https://api.ulink.ly/v1"

2. Track Installation (Required)

When to call: Once per app installation/device, or when your installation token expires.

curl -X POST "$ULINK_BASE_URL/sdk/installations/track" \
-H "X-App-Key: $ULINK_API_KEY" \
-H "Content-Type: application/json" \
-H "X-ULink-Client: api-direct" \
-H "X-ULink-Client-Version: 1.0.0" \
-H "X-ULink-Client-Platform: ios" \
-d '{
"installationId": "unique-installation-id-from-your-app",
"deviceId": "device-identifier",
"deviceModel": "iPhone 15 Pro",
"deviceManufacturer": "Apple",
"osName": "iOS",
"osVersion": "17.0",
"appVersion": "1.0.0",
"appBuild": "100",
"language": "en",
"timezone": "America/New_York"
}'

Response includes installation token:

{
"success": true,
"installationId": "db-record-id",
"isNew": true,
"installationToken": "eyJhbGciOiJIUzI1NiIs..."
}

Response Headers:

X-Installation-Token: eyJhbGciOiJIUzI1NiIs...

⚠️ Important: Save the installationToken securely in your app (SharedPreferences, UserDefaults, etc.). This token identifies your installation for 90-180 days.

3. Start Session (Required)

When to call: When user opens your app or becomes active.

curl -X POST "$ULINK_BASE_URL/sdk/sessions/start" \
-H "X-App-Key: $ULINK_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Installation-Token: eyJhbGciOiJIUzI1NiIs..." \
-H "X-ULink-Client: api-direct" \
-H "X-ULink-Client-Version: 1.0.0" \
-H "X-ULink-Client-Platform: ios" \
-d '{
"installationId": "unique-installation-id-from-your-app",
"deviceOrientation": "portrait",
"networkType": "wifi",
"batteryLevel": 85,
"isCharging": false
}'

Response:

{
"success": true,
"sessionId": "session-uuid"
}
curl -X POST "$ULINK_BASE_URL/sdk/links" \
-H "X-App-Key: $ULINK_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Installation-Token: eyJhbGciOiJIUzI1NiIs..." \
-d '{
"type": "unified",
"slug": "my-custom-link",
"fallbackUrl": "https://example.com",
"iosUrl": "https://apps.apple.com/app/id123",
"androidUrl": "https://play.google.com/store/apps/details?id=com.app",
"metadata": {
"ogTitle": "Check out this app",
"ogDescription": "The best app for managing your links",
"ogImage": "https://example.com/images/preview.jpg"
}
}'

Response:

{
"id": "link-id",
"slug": "my-custom-link",
"shortUrl": "https://yourdomain.com/my-custom-link",
"type": "unified",
"fallbackUrl": "https://example.com",
"createdAt": "2024-01-01T00:00:00Z"
}

Query-Parameter Passthrough

Pass "allowQueryPassthrough": true in the request body to let a link forward query params appended to its URL into the app's parameters map. The field is false by default and applies to both POST /sdk/links (create) and link update endpoints.

curl -X POST "$ULINK_BASE_URL/sdk/links" \
-H "X-App-Key: $ULINK_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Installation-Token: eyJhbGciOiJIUzI1NiIs..." \
-d '{
"type": "dynamic",
"slug": "checkout",
"fallbackUrl": "https://example.com",
"parameters": { "screen": "checkout" },
"allowQueryPassthrough": true
}'

When a user opens https://links.shared.ly/checkout?orderId=123, the app receives parameters: { "screen": "checkout", "orderId": "123" }. Passthrough values are strings and override stored params with the same key.

Server-side validation envelope (applied only when passthrough is enabled; invalid params are silently dropped):

RuleLimit
Key format[A-Za-z0-9_-], 1–64 characters
Max params25
Max value length1024 characters
Max total payload4 KB
Reserved keysdebug, _qr, _nodl, ulhop, d, and prototype-chain keys (__proto__, constructor, prototype) are dropped

See the Query-Parameter Passthrough guide for end-to-end examples and SDK-side reading instructions.

ulhop and d are internal to the launch-domain redirect flow (used by the iosSocialBreakout behavior below). Appending ?ulhop=1 to any link redirects to that link's iOS fallback URL and does not record a click.

iOS In-App Browser Behavior

Two boolean fields control how a link behaves on iOS. Both are set in the dashboard, on the link's edit screen — neither is accepted by POST /sdk/links, which ignores them if sent:

  • iosSocialBreakout — when the link is opened on iOS in any browser (Safari, Chrome, or an in-app webview such as TikTok, Instagram or Facebook), the redirect page sends the visitor to a ULink launch domain that shows an Open App button and a Download from App Store button. The user must tap the button; nothing opens automatically. Only applies to dynamic links. On by default when the project's iOS app configuration is complete (Team ID and bundle identifier — these publish the Universal Link association that Open App depends on); off when it is not, since no tap could reach the app. Set it to false on a link to opt that link out.
  • iosBrowserAutoOpen — when the link is opened in a real iOS browser (Safari, Chrome, Firefox, Edge — not an in-app webview), the redirect page automatically tries the app's custom URL scheme and falls back to the link's iOS fallback URL after roughly 2.5 seconds. false by default.

When both are on, iosBrowserAutoOpen wins in a real browser. The interstitial is the better mechanism — a Universal Link rather than a custom scheme that shows a browser error dialog when the app is missing — but auto-open is an explicit per-link choice, and iosSocialBreakout is usually an implicit default, so the default does not override it. In-app webviews are unaffected: auto-open never runs there, so they still get the interstitial.

curl -X POST "$ULINK_BASE_URL/sdk/links" \
-H "X-App-Key: $ULINK_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Installation-Token: eyJhbGciOiJIUzI1NiIs..." \
-d '{
"type": "dynamic",
"slug": "promo",
"fallbackUrl": "https://example.com",
"iosFallbackUrl": "https://apps.apple.com/app/id123"
}'

Social Media Tags (Open Graph)

Use the metadata field to control how your links appear when shared on social media platforms like Facebook, Twitter/X, LinkedIn, and messaging apps like WhatsApp and iMessage.

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

When a social media crawler fetches your link, Ulinkly automatically renders the appropriate <meta> tags:

<meta property="og:title" content="Check out this app">
<meta property="og:description" content="The best app for managing your links">
<meta property="og:image" content="https://example.com/images/preview.jpg">
<meta name="twitter:card" content="summary_large_image">
tip

If no metadata is provided, Ulinkly uses the link's name as a fallback title and generates a default description based on the link type.

curl -X GET "$ULINK_BASE_URL/sdk/resolve?url=https://yourdomain.com/your-slug" \
-H "X-App-Key: $ULINK_API_KEY" \
-H "X-Installation-Token: eyJhbGciOiJIUzI1NiIs..." \
-H "X-ULink-Client: api-direct" \
-H "X-ULink-Client-Version: 1.0.0" \
-H "X-ULink-Client-Platform: ios"

Response:

{
"id": "link-id",
"slug": "your-slug",
"type": "unified",
"fallbackUrl": "https://example.com",
"iosUrl": "https://apps.apple.com/app/id123",
"androidUrl": "https://play.google.com/store/apps/details?id=com.app"
}

Response Headers:

X-Installation-Token: eyJhbGciOiJIUzI1NiIs...  # Refreshed token if needed
X-ULink-Context: installation-attached|auto-installed|none
X-ULink-Warning: missing_device_id # Only if no installation context

When to call: When user backgrounds the app or becomes inactive.

curl -X POST "$ULINK_BASE_URL/sdk/sessions/{sessionId}/end" \
-H "X-App-Key: $ULINK_API_KEY" \
-H "X-Installation-Token: eyJhbGciOiJIUzI1NiIs..."

Response:

{
"success": true
}

Installation Token Management

What is an Installation Token?

An installation token is a JWT (JSON Web Token) that:

  • Securely identifies your app installation
  • Expires after 90-180 days
  • Eliminates the need to send installationId in every request
  • Prevents duplicate installations when upgrading client versions

Token Management Flow

  1. App Launch: Check for saved token
  2. No Token: Call /sdk/installations/track to get one
  3. Has Token: Use it in API requests
  4. Token Expired: Refresh by calling /sdk/installations/track again

Required Headers for Direct Integration

All Ulinkly SDK endpoints require these headers:

# Required
X-App-Key: your-api-key-here
Content-Type: application/json

# Recommended for analytics and proper tracking
X-ULink-Client: api-direct
X-ULink-Client-Version: 1.0.0
X-ULink-Client-Platform: ios|android|web|server

# Optional (for old SDK compatibility)
X-Installation-Id: your-installation-id # Fallback if no token
X-Device-Id: your-device-id # For legacy SDK versions

# Required after installation tracking
X-Installation-Token: eyJhbGciOiJIUzI1NiIs...

Error Handling

Common Error Responses

StatusErrorSolution
401Invalid or missing API keyCheck X-App-Key header
400API key is requiredInclude valid X-App-Key header
400URL parameter is requiredInclude url query parameter for resolve
404Link with slug 'x' not foundVerify the URL/slug exists
403Usage limit exceededCheck your plan limits

Important Response Headers

X-Installation-Token: eyJhbGciOiJIUzI1NiIs...  # Save this token
X-ULink-Context: installation-attached|auto-installed|none
X-ULink-Warning: missing_device_id # Installation context missing

Best Practices

1. Installation Tracking

  • Call /sdk/installations/track once per app installation
  • Save the returned token securely
  • Refresh token only when it expires (90-180 days)

2. Session Management

  • Start session when app becomes active
  • End session when app goes to background
  • Don't start multiple sessions simultaneously

3. Token Handling

  • Always include X-Installation-Token in requests after tracking installation
  • Automatically retry with a fresh token after a 401 Unauthorized response
  • Store token securely (SharedPreferences, UserDefaults, etc.)

4. Error Recovery

  • Implement automatic retry for network failures
  • Refresh tokens on authentication errors
  • Queue operations when offline

Migration from Basic API Usage

If you're currently using only /sdk/resolve:

Add Installation Tracking

# First, track the installation
curl -X POST https://api.ulink.ly/v1/sdk/installations/track \
-H "X-App-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"installationId": "unique-id", ...}'

# Save the returned installationToken

Add Session Management

# Start session for active users
curl -X POST https://api.ulink.ly/v1/sdk/sessions/start \
-H "X-App-Key: YOUR_API_KEY" \
-H "X-Installation-Token: SAVED_TOKEN" \
-H "Content-Type: application/json" \
-d '{"installationId": "unique-id", ...}'

Updated Resolve Calls

# Now include the installation token
curl -X GET "https://api.ulink.ly/v1/sdk/resolve?url=YOUR_URL" \
-H "X-App-Key: YOUR_API_KEY" \
-H "X-Installation-Token: SAVED_TOKEN"

Troubleshooting

Common Issues

"Installation context missing" warnings

Symptoms: Getting X-ULink-Warning: missing_device_id headers
Solution: Always include X-Installation-Token or call /sdk/installations/track first

Sessions not tracking properly

Symptoms: Dashboard shows fewer active users than expected
Solution: Ensure you're calling /sdk/sessions/start for active users

Symptoms: 404 error when creating links
Solution: Ensure your project has a verified domain configured

Token expiration errors

Symptoms: 401 Unauthorized errors after working for months Solution: Implement automatic token refresh when errors occur

Debug Checklist

  • API key is valid and in X-App-Key header
  • Installation tracking called at least once
  • Installation token saved and included in requests
  • Sessions started for active users
  • Error handling with token refresh implemented

Next Steps

  1. Implement the complete flow with installation tracking and session management
  2. Test MAU tracking with a small user group
  3. Monitor usage in the Ulinkly dashboard
  4. Consider an official SDK for automatic handling of these concerns

Support

Consider SDKs

Our official SDKs handle all this complexity automatically. This guide is for custom implementations only.