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.
- 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:
- Installation Tracking: Identify unique app installations
- 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"
}
4. Create Links (Optional)
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):
| Rule | Limit |
|---|---|
| Key format | [A-Za-z0-9_-], 1–64 characters |
| Max params | 25 |
| Max value length | 1024 characters |
| Max total payload | 4 KB |
| Reserved keys | debug, _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 tofalseon 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.falseby 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.
| Field | Description | Recommended |
|---|---|---|
ogTitle | Title shown in social previews | Up to 60 characters |
ogDescription | Description shown in social previews | Up to 155 characters |
ogImage | Image URL shown in social previews | 1200x630px, 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">
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.
5. Resolve Links (Optional)
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
6. End Session (Recommended)
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
installationIdin every request - Prevents duplicate installations when upgrading client versions
Token Management Flow
- App Launch: Check for saved token
- No Token: Call
/sdk/installations/trackto get one - Has Token: Use it in API requests
- Token Expired: Refresh by calling
/sdk/installations/trackagain
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
| Status | Error | Solution |
|---|---|---|
| 401 | Invalid or missing API key | Check X-App-Key header |
| 400 | API key is required | Include valid X-App-Key header |
| 400 | URL parameter is required | Include url query parameter for resolve |
| 404 | Link with slug 'x' not found | Verify the URL/slug exists |
| 403 | Usage limit exceeded | Check 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/trackonce 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-Tokenin requests after tracking installation - Automatically retry with a fresh token after a
401 Unauthorizedresponse - 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
Link creation fails
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-Keyheader - 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
- Implement the complete flow with installation tracking and session management
- Test MAU tracking with a small user group
- Monitor usage in the Ulinkly dashboard
- Consider an official SDK for automatic handling of these concerns
Support
- API Questions: REST API overview
- SDK Options: Operating System Integration
- Issues: Contact Support
Our official SDKs handle all this complexity automatically. This guide is for custom implementations only.