Skip to main content

REST API Reference

These endpoints are the same ones the OpenLynk mobile SDKs call. You can also call them directly from your backend (admin panels, invite services, scripts) using your app's SDK API key.

:::tip Common use case Invite users from an admin panel: create an Openlynk link with POST /api/links/create-sdk, put link.url in the email, and let Openlynk handle open-app vs download. See Create invite links from your server. :::

Base URL: https://openlynk.io

Authentication

Most endpoints require a per-app API key via either header:

x-openlynk-sdk-key: ol_your_api_key_here

or:

Authorization: Bearer ol_your_api_key_here
DetailValue
Key formatol_ + 32 hex characters
ScopeSingle app
Where to createDashboard → your app → SDK API Key
appIdDerived from the key for authenticated routes — do not rely on body appId for auth
info

Keys are stored as SHA-256 hashes. Openlynk never stores plaintext. If you lose a key, rotate it in the dashboard.

:::warning Server-side use When calling these endpoints from a backend, keep the API key in a secrets manager or environment variable. Never commit it to source control. The same key may also be embedded in your mobile app — treat it as sensitive and rotate it if leaked. Dedicated server-only keys are planned as a follow-up. :::

Quick reference

MethodEndpointAuthRate limit
POST/api/links/create-sdkAPI key10 req/min/IP
GET/api/links/get-by-slugNone30 req/min/IP
POST/api/deferred-deep-linking/restoreAPI key20 req/min/IP
POST/api/deferred-deep-linking/install-heartbeatAPI key10 req/min/IP
POST/api/deferred-deep-linking/storeAPI key20 req/min/IP
POST/api/push/registerAPI key20 req/min/IP
POST/api/push/openedAPI key30 req/min/IP

POST /api/links/create-sdk

Creates a dynamic deep link. This is the primary endpoint for in-app sharing and server-side invite links.

Headers:

Content-Type: application/json
Authorization: Bearer ol_your_api_key_here

Request body:

{
"destination": "/invite/abc123",
"metadata": {
"invite_id": "abc123",
"utm_source": "email",
"utm_medium": "invite"
}
}
FieldTypeRequiredDescription
destinationstringYesRelative path starting with / (query string allowed). Must not start with //.
metadataobjectNoCustom key-value data attached to the link (UTM params, invite IDs, etc.)

The server generates the link slug automatically ({timestamp}-{random}).

curl example:

curl -X POST https://openlynk.io/api/links/create-sdk \
-H "Authorization: Bearer ol_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"destination": "/invite/abc123",
"metadata": {
"invite_id": "abc123",
"utm_source": "email"
}
}'

Response (200):

{
"success": true,
"link": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"slug": "1734567890-a1b2c3d4",
"url": "https://myapp.openlynk.to/1734567890-a1b2c3d4",
"destination": "/invite/abc123",
"metadata": {
"invite_id": "abc123",
"utm_source": "email",
"destination_path": "/invite/abc123",
"destination_full": "/invite/abc123",
"created_via": "sdk"
}
}
}

Use link.url in emails, SMS, or any share channel. When the recipient taps it, Openlynk opens the app if installed, or routes them to install and restores the destination via deferred deep linking.

Errors:

StatusWhen
400Missing or invalid destination
401Missing or invalid API key
403Generated-link plan limit reached (GENERATED_LINKS_LIMIT_REACHED)
404App not found
429Rate limited

GET /api/links/get-by-slug

Resolves a link slug to destination and metadata. Used by SDKs when handling an incoming deep link.

Auth: Not required. Rate-limited by IP.

Query parameters:

ParamRequiredDescription
slugYesLink slug
appIdConditionalApp UUID. Required unless hostname can resolve the app
hostnameConditionalHost used to resolve the app (platform subdomain or custom domain) when appId is omitted

Example:

curl "https://openlynk.io/api/links/get-by-slug?slug=1734567890-a1b2c3d4&appId=YOUR_APP_ID"

Response (200):

{
"success": true,
"link": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"slug": "1734567890-a1b2c3d4",
"destination_web_url": "/invite/abc123",
"destination_path": "/invite/abc123",
"destination": "/invite/abc123?invite_id=abc123",
"parameters": {
"invite_id": "abc123",
"utm_source": "email"
},
"metadata": { }
}
}

Deferred deep linking

POST /api/deferred-deep-linking/restore

Restores pending links after install (or on SDK init). Prefer calling this from the mobile SDK; documented here for parity and advanced integrations.

Request body:

{
"userEmail": "[email protected]",
"deviceFingerprint": "fp_abc123def456"
}
FieldTypeRequiredDescription
userEmailstringOne of email or fingerprintEmail to match pending links
deviceFingerprintstringOne of email or fingerprintClient device fingerprint

appId is taken from the API key.

Response (200):

{
"success": true,
"restoredLinks": [
{
"pendingLinkId": "pending-link-uuid",
"originalUrl": "https://myapp.openlynk.to/abc123",
"destination_url": "/product/123?ref=share",
"destination": "/product/123?ref=share",
"destination_path": "/product/123",
"metadata": { "campaign": "summer-sale" },
"parameters": { "campaign": "summer-sale" },
"linkId": "550e8400-e29b-41d4-a716-446655440000"
}
],
"count": 1,
"message": "Restored 1 pending links"
}

Matching priority: email → device fingerprint → IP / user-agent fallbacks on the server.


POST /api/deferred-deep-linking/install-heartbeat

Reports an install or heartbeat. The SDK throttles this to about once per 24 hours per device.

Request body:

{
"deviceFingerprint": "fp_abc123def456"
}
FieldTypeRequiredDescription
deviceFingerprintstringYesDevice fingerprint

Response (200):

{
"success": true,
"isFirstLaunch": true
}

POST /api/deferred-deep-linking/store

Stores a pending link for deferred deep linking. Openlynk's link resolver usually does this automatically when a user clicks a link without the app installed. You rarely need to call this yourself.

Request body:

{
"linkId": "550e8400-e29b-41d4-a716-446655440000",
"userEmail": "[email protected]",
"originalUrl": "https://myapp.openlynk.to/abc123"
}
FieldTypeRequiredDescription
linkIdstringYesID of the clicked link
originalUrlstringNoFull URL the user clicked
userEmailstringNoUser email if known

Response (200):

{
"success": true,
"pendingLinkId": "pending-link-uuid"
}

Push notifications

POST /api/push/register

Registers a device push token (FCM / APNs). Called by the mobile SDK.

Request body:

{
"deviceToken": "fcm-or-apns-device-token",
"platform": "android",
"userEmail": "[email protected]"
}
FieldTypeRequiredDescription
deviceTokenstringYesFCM or APNs device token
platformstringYes"ios" or "android"
userEmailstringNoAssociate the token with a user
note

The field name is deviceToken (not token).

Response (200):

{
"success": true
}

POST /api/push/opened

Logs that a user opened a push notification (analytics).

Request body:

{
"notificationId": "notification-uuid",
"deviceToken": "fcm-or-apns-device-token"
}
FieldTypeRequiredDescription
notificationIdstringYesID of the push notification
deviceTokenstringNoDevice token that opened it

Response (200):

{
"success": true
}

POST /api/push/send

Sends a push notification to registered devices.

warning

This endpoint requires dashboard session authentication, not an SDK API key. It is not for client or SDK-key backends.


Error responses

Authenticated endpoints return JSON errors:

{
"error": "Description of the error"
}
StatusMeaning
400Invalid request body or query params
401Missing or invalid API key
403Plan limit reached (e.g. generated links)
404Resource not found
429Rate limited
500Server error

On link creation plan limits, the body may also include limitReached, upgradeRequired, currentCount, and limit.

What's next?