Skip to content
Sendora Cloud
Create account
Module deep-dive

Deep Links — create & resolve

Mint branded short links at runtime with a custom linkData payload, then read that payload back when the recipient opens the link — including on a cold install (deferred deep linking). Works from any client over REST, or via the native SDKs where available.

Create a link (REST)

POST /api/v1/sdk/links is the programmatic mint surface. It works identically from a mobile app or your backend. Authenticate with a publishable key (pk_…) in the x-api-key header — no session, no org role, no projectId in the body (it's inferred from the key).

curl -X POST https://api.sendoracloud.com/api/v1/sdk/links \
  -H "x-api-key: pk_live_YOUR_PUBLISHABLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Challenge from Alice",
    "iosDeepLinkPath": "/challenge",
    "linkData": { "token": "abc123" }
  }'

Only title is required. Attach your own payload as the top-level linkData object (arbitrary JSON, up to 2 KB serialized). The shortcode is auto-generated (squatting defense — no custom codes on this route). Response 201:

{
  "success": true,
  "data": {
    "id": "…",
    "shortcode": "aB3xK9",
    "url": "https://go.sendoracloud.com/aB3xK9",
    "iosDeepLinkPath": "/challenge",
    "linkData": { "token": "abc123" },
    "expiresAt": null
  }
}

Share data.url. If you send the optional iosBundleId, it must match a registered app or the mint is refused; omit it and the mint always succeeds.

Which key — publishable or secret?

  • Publishable pk_ — the client-safe key, designed to ship in your app bundle. It has the links:write scope by default, so it can mint from the app directly. /api/v1/sdk/links requires a pk_ and rejects secret keys.
  • Secret sk_ — use only from your own backend, against the dashboard route POST /api/v1/orgs/:orgId/projects/:projectId/links (supports custom shortcodes + 10 KB linkData, and has no per-key rate limit). It returns the raw link row — build the URL yourself as https://go.sendoracloud.com/<shortcode>.

Rate limit: /sdk/links is capped at 60 mints/minute per device (by IP) — each user's device gets its own budget, so a viral “invite a friend” flow from the app scales fine. (The secret-key backend route has no per-key limit if you need very high sustained volume.)

The linkData payload

linkData is stored as JSON and returned byte-for-byte on resolve — nested objects and arrays survive intact, no reserved keys, no type coercion. An omitted linkData comes back as {} (not null). Size cap is the only limit: 2 KB via /sdk/links, 10 KB via the dashboard route.

Resolve a link (read the payload back)

To read linkData back you need the authenticated resolve, which calls GET /api/v1/sdk/links/:shortcode (scope links:read, also default on pk_):

// iOS — authenticated resolve carries linkData back:
SendoraCloud.links?.handleUniversalLink(url: url) { data in
    let token = data?.linkData["token"] as? String
}

// Cold install (deferred) — probabilistic IP-hash match, 2-hour window:
SendoraCloud.checkDeferredDeepLink { data in
    let token = data?.linkData["token"] as? String
}

Note: the purely local handleDeepLink(url:) is an offline URL parse — it returns only the shortcode, not linkData. Use handleUniversalLink / checkDeferredDeepLink to get the payload.

Native SDK create()

The iOS SDK also exposes SendoraCloud.links?.create(_:prewarmKey:completion:) (a convenience wrapper over the REST call above). It's available on iOS 15 from SDK 4.6.0 — 3.9.0–4.5.0 required iOS 16 (the whole package was pinned there for passkeys; 4.6.0 @available-gated passkeys and restored the iOS 15 floor). The REST endpoint above works on any iOS version regardless. There is no server-side idempotency — prewarmKey is a client-side cache hint only, so dedupe on your side if two identical calls must not create two links.

Domain, AASA & deferred install

The default link host is go.sendoracloud.com (a verified custom domain overrides it per project). For warm (installed-app) opens via Universal Links:

  1. Register your iOS app with both its bundle ID and Apple Team ID (missing either → dropped from the AASA file).
  2. Have at least one active link with an iosDeepLinkPath (the global AASA only lists apps that do).
  3. Add applinks:go.sendoracloud.com to your Associated Domains entitlement in Xcode.

Deferred cold-install does not use AASA — it runs through POST /api/v1/sdk/links/match (probabilistic IP-hash on iOS, 2-hour window), and needs only your configured pk_. If you move off the default host, override the SDK's linkHosts allowlist or inbound URLs are rejected.

Project scoping

projectId is inferred from the API key — you never pass it on the SDK/REST path. The key must be project-scoped (an org-wide pk_ with no project is rejected); use the per-project key Sendora auto-mints. configure(projectId:) is optional on SDK 3.0.0+.