Home Developer Reference

Developer Reference

Architecture, API, auth, and operational reference docs for LinkMe developers
By Tomas Radvansky
8 articles

Platform Architecture

import MermaidChart from '@site/src/components/MermaidChart'; Platform architecture LinkMe runs as a compact managed edge stack. Every request flows through a small set of services, which keeps deployments predictable and lets SDKs rely on well-defined behaviors. High-level flow <MermaidChart chart={graph LR User[User / Device] --> Entry[Global edge entrypoint] Entry -->|Admin UI| Portal[Portal] Entry -->|Links & config| Resolver[Link resolver] Resolver --> Data[(Managed datastore)] Portal --> Data} /> Global edge entrypoint - Terminates TLS, enforces routing rules, and fronts both the admin portal and public link resolver. - Hosts Universal Links/App Links association files (apple-app-site-association, assetlinks.json) so mobile OSes can trust your domains. Link resolver service - Resolves short links, applies platform routing rules, and records click analytics. - Exposes REST endpoints such as /:slug, /d/:slug, /qr/:slug, and the JSON helpers under /api/*. - Maintains device fingerprints (cid, IP hash, UA hash) for deferred deep linking claims. Portal - Provides the admin experience for apps, domains, SDK keys, and reporting. - Issues API keys, allows toggle of features such as pasteboard support, and surfaces click analytics. - Offers marketing pages and articles under /resources/* for discoverability. SDKs - Ship per platform (Android, iOS, React Native, Flutter, Web, Node). - Handle initial link opens, in-app listeners, deferred claims, and optional analytics events. - Communicate only with the Edge endpoints—no direct datastore or Portal access is required. Deployment model - Managed infrastructure keeps the edge entrypoint, link resolver, and portal running in lockstep. - Custom domains CNAME into the edge so certificates can be provisioned automatically. Data & observability - Click and link metadata stay inside the managed datastore powering both portal insights and the resolver. - Health probes hit GET /api/health and expect { ok: true }. - Logs stream centrally for troubleshooting and incident response. Use this page with the REST Endpoints & OpenAPI doc to understand how the platform responds in production without exposing internal implementation details.

Last updated on Apr 04, 2026

API Keys & Authentication

API keys & authentication The Edge API trusts signed requests from your LinkMe app credentials. Keys are issued and managed inside the Portal so you can rotate them without rebuilding the SDKs. API access is available on paid plans. On the Free plan, requests are rejected with error: paid_plan_required and upgrade_url: https://li-nk.me/portal/account. Create and rotate keys in the Portal, then pair this page with the pricing overview if you are deciding which plan should hold production domains, team seats, and automation workflows. Key types | Key type | Use case | Capabilities | | --- | --- | --- | | App API Key | Bundled with a specific app and used by SDKs to resolve links or claim deferred payloads. | can_read by default; optionally granted can_write for server-side automation. | | Server/API Key | Generated per workspace for CLI scripts or backend jobs (create links, sync metadata). | Typically can_write; protect it like any other secret. | Each key stores: - appId – immutable identifier for the app/workspace. - appKey – secret value you send as x-api-key. - Capability flags (can_read, can_write). - Optional expiry and label so you remember why it exists. Sending credentials SDK endpoints accept lightweight headers, while admin APIs additionally support a Bearer token when you are automating from a trusted server. GET /api/deeplink?cid=abc123 HTTP/1.1 Host: li-nk.me x-app-id: YOUR_APP_ID x-api-key: YOUR_APP_KEY For write operations (creating links, rotating domains) use your server/API key and the Authorization header: curl https://li-nk.me/api/links \ -H 'Authorization: Bearer sk_live_...' \ -H 'Content-Type: application/json' \ -d '{"slug":"spring","ios_store_url":"https://apps.apple.com/..."}' Rotation & hygiene 1. Name keys clearly – include the environment, app, and purpose so you know what to delete later. 2. Avoid embedding write keys in mobile apps – SDKs should only ship read scopes; keep write access on servers. 3. Rotate on a schedule – create a new key, deploy it, then remove the old one in Portal. 4. Monitor usage – Portal shows last used timestamps; revoke anything that looks idle or unexpected. Pair this page with the REST Endpoints & OpenAPI doc to understand how the credentials interact with the rest of the system. Contact support if you need deeper architectural artifacts for vendor reviews. If you are validating end-to-end routing after creating keys, the Universal Links Validator and developer setup overview are the fastest next steps. For Model Context Protocol integrations, follow the MCP Security & Access Model so tooling remains scoped to normal REST permissions.

Last updated on Apr 09, 2026

REST Endpoints & OpenAPI

REST endpoints & OpenAPI The Edge service exposes public REST endpoints for short-link creation, resolution, analytics claims, and health checks. Explore them via Swagger UI or download the OpenAPI document for client generation. Live specification - OpenAPI JSON — Download - Swagger UI — View interactive docs (If you host LinkMe yourself, serve apps/edge/openapi.json behind your own domain.) Interactive Swagger testing - Drop your App ID and App Key into the credential helper at the top of the Swagger page. They stay inside your browser and are reused for every Try it out request. - Use read-only keys for link resolution / deferred claim endpoints. Supply a server token if you want to demo POST /s or other admin routes. - Clicking Clear stored credentials wipes the local storage copy instantly. Key endpoints | Method & path | Description | | --- | --- | | POST /s | Create a short link (used by the marketing site’s short-link builder). | | GET /:slug | Resolve a link, performing platform detection/redirect. | | GET /d/:slug | Hosted fallback landing page for each link. | | GET /qr/:slug | Return a QR code (SVG/PNG) tied to the link. | | GET /api/deeplink?cid=... | Claim the full payload for a given click token. | | POST /api/deferred/claim | Fingerprint-based claim when no token is available. | | GET /api/health | Lightweight health probe. | Link resolution behavior All redirect behavior is captured in the OpenAPI spec, but the highlights are: - GET /:slug - Detects platform from User-Agent and computes destination: - iOS: ios_custom_url → ios_store_url → deep link (https://host/<deep_link_path>) - Android: android_custom_url → android_store_url → deep link - Desktop: web_fallback_url or app-level web_fallback_url, else /d/:slug - force_redirect_web pushes all traffic to the web fallback when present. - UTM handling: allow_param_passthrough=1 treats stored utm_* fields as defaults. Keys flagged in utm_override prefer incoming query parameters. - Crawler UAs receive a lightweight OpenGraph HTML response unless debug mode is requested. - Injects cid into custom URL schemes; Play Store deep links include referrer=cid%3D... so Install Referrer can recover the click. - Every hit records analytics and issues a 302 redirect. Append + (/:slug+) for a debug page showing computed targets without redirecting. - GET /d/:slug - Hosted download landing page with App Store / Play buttons plus a QR code pointing at the canonical slug. - GET /qr/:slug - Returns PNG when Accept includes image/png, otherwise SVG. Cache headers mirror the slug’s TTL. Campaign chaining example 1. Create a marketing slug (e.g. fb-easter) with force_redirect_web=1, allow_param_passthrough=1, and a web_fallback_url pointing at https://foo.com/easter. Store baseline UTM values (utm_source=fb, utm_medium=social, utm_campaign=easter). 2. Place a download button on foo.com/easter that links to a second li-nk.me slug (e.g. download-easter) configured for deferred deep linking to the stores/apps. Leave allow_param_passthrough=1 so it merges the original UTM set with additional landing-specific keys (e.g. utm_medium=web, utm_content=landing). 3. When a visitor arrives through the campaign slug, they retain the Facebook UTM payload on the landing page. Clicking the download button propagates the original attribution and augments it with the landing metadata for downstream analytics. Client generation Use the OpenAPI doc to generate REST clients: npx openapi-typescript https://li-nk.me/docs/api/openapi.json -o edge.d.ts npx openapi-generator-cli generate \ -i https://li-nk.me/docs/api/openapi.json \ -g typescript-fetch \ -o ./generated/edge Regenerate whenever the portal announces new endpoints in the changelog. For secure MCP integrations that consume these endpoints, see MCP Security & Access Model.

Last updated on Apr 04, 2026

Developer Overview

Developer overview Welcome to the technical half of the docs. This section gives you two tracks: - Global architecture & APIs – how the edge service, routing rules, health probes, and credentials behave. - Platform SDKs – per-platform setup plus deeper reference material. Use the map below to decide where to jump next. How to use this section efficiently If you are integrating LinkMe into an existing app stack, start with authentication and endpoint behavior before you touch SDK code. That gives you a clear contract for keys, scopes, error handling, and routing outcomes. Once that baseline is clear, move into platform setup for iOS, Android, or cross-platform runtimes. If you are migrating from another deep linking provider, treat the docs as a cutover checklist: 1. Validate endpoint and key model in REST Endpoints & OpenAPI. 2. Confirm route and fallback semantics with API Keys & Authentication. 3. Configure platform-specific SDK setup in /developer/setup. 4. Add event delivery checks in Webhooks API. 5. Run parity QA on click, open, and deferred flows before traffic migration. Architecture & APIs - Architecture diagrams – available from support upon request for security and compliance reviews. - Link routing & QR behavior – see REST Endpoints & OpenAPI for platform detection, debug tooling, and passthrough semantics. - /api/health contract – documented inside the REST Endpoints & OpenAPI spec. - REST Endpoints & OpenAPI – download the public spec or explore Swagger UI. - API Keys & Authentication – header formats, capability flags, and rotation tips. - MCP Security & Access Model – guardrails that ensure MCP cannot exceed REST permissions. - Webhooks API – event types, delivery behavior, retries, signatures, and portal management. Platform setup & SDKs - Developer Setup Hub – pick iOS, Android, React Native, Flutter, Web, or Node quickstarts. - Android SDK - iOS SDK - React Native SDK - Flutter SDK - Web SDK - Node SDK Each SDK page includes installation, initialization, listeners, deferred link handling, and analytics helpers. Recommended integration sequence To reduce rollout risk, use the same sequence across all platforms: - Configure app identity and domain association details. - Add SDK initialization and listener handling. - Test direct opens, deferred opens, and fallback behavior on real devices. - Validate attribution fields and event payload shape. - Enable webhook delivery only after the core routing paths are stable. Following this order prevents a common failure mode where teams debug analytics or webhook behavior before confirming that base link resolution is correct. Bookmark this page whenever you need orientation between higher-level concepts and SDK-specific deep dives.

Last updated on Apr 04, 2026

MCP Security & Access Model

MCP security & access model LinkMe treats MCP as a strict adapter over the Edge REST API, not as a privileged backend. Security invariant For any caller principal (session, app key, or MCP key): Allowed(MCP) ⊆ Allowed(REST) If a request would be forbidden over REST, it must also be forbidden via MCP. What MCP is allowed to do - Call only explicitly allowlisted public /api/* endpoints. - Use the same authentication context as the caller: - session/cookie context for portal users - app API key context for app-scoped automation - MCP key context for personal or team-scoped access - Enforce app scope and capability checks before forwarding requests. What MCP is not allowed to do - Access /internal/* endpoints. - Bypass auth, ownership, or tenant boundaries. - Use elevated service credentials to impersonate broader access. - Call undocumented/unallowlisted tools. Authentication The MCP endpoint authenticates callers via HTTP headers on every request. Three modes are supported: MCP API key (recommended) MCP keys can be personal (no team) or team-scoped. Generate one in the portal under Team → MCP Keys. Available on the Indie plan and above. | Header | Value | |---|---| | Authorization | Bearer tk_... | The key acts as the user who created it. Team-scoped keys grant access to all apps within that team; personal keys grant access to the user's own apps. can_read / can_write capabilities control which tools are available. The key prefix tk_ distinguishes MCP keys from app keys (ak_). Tool visibility differs by key type: - Team-scoped keys see team tools (teams.get, apps.listByTeam) with the team ID auto-filled from the key. They do not see apps.list. - Personal keys see apps.list (returns all apps the user owns or has access to). They do not see team-scoped tools. App API key Scoped to a single app. Pass X-App-Id and X-Api-Key headers. Generate in App → Developer → API Keys. Session cookie Portal users already signed in can authenticate via their session cookie. Primarily useful for browser-based MCP clients. Plan enforcement - MCP access is enforced at call time and follows plan entitlements. - Free plan users may still attempt tool calls, but the server rejects them with: - error: paid_plan_required - reason: mcp_access_requires_paid_plan - upgrade_url: https://li-nk.me/portal/account - This keeps tool discovery and request flow predictable while ensuring execution stays aligned with billing entitlements. Available tools | Tool | Method | Path | Access | Auth modes | |---|---|---|---|---| | health.get | GET | /api/health | read | session, app_key, team_key | | teams.get | GET | /api/teams/:id | read | session, team_key | | apps.list | GET | /api/apps | read | session, team_key | | apps.listByTeam | GET | /api/teams/:id/apps | read | session, team_key | | apps.get | GET | /api/apps/:id | read | session, app_key, team_key | | links.listByApp | GET | /api/apps/:id/links | read | session, app_key, team_key | | links.getInsights | GET | /api/links/:id/insights | read | session, app_key, team_key | | links.getDetails | GET | /api/link-details | read | session, app_key, team_key | | links.create | POST | /api/apps/:id/links | write | session, app_key, team_key | | links.update | PATCH | /api/links/:id | write | session, app_key, team_key | Write tools are disabled by default and require MCP_WRITE_ENABLED=1 on the server. Access controls - Tool allowlist: each MCP tool is pinned to one REST method and path template. - Auth mode restrictions: tools define which auth modes (session, app_key, team_key) are accepted. - Capability checks: - Read tools require can_read capability. - Write tools require can_write capability. - App scope checks: app-key tools that target /api/apps/:id/* must match the key's app_id. MCP keys skip this check — app ownership is enforced by the REST layer via the synthesized session. - Team ID auto-fill: team-scoped MCP keys automatically inject the team ID into team-scoped tool paths (/api/teams/:id/*), so the caller does not need to provide it. - Tool filtering: personal MCP keys only see personal tools; team-scoped keys only see team tools. This prevents the LLM from calling tools it cannot authenticate against. - Write gate: write tools are disabled by default and require MCP_WRITE_ENABLED=1. Validation and parity testing LinkMe includes security tests that simulate natural-language asks and protocol calls, then verifies policy and REST parity: - question-to-tool security scenarios (deny escalation attempts) - MCP protocol tests (initialize, tools/list, tools/call) - MCP-vs-REST status parity for the same principal and target resources - personal key and team key authorization scenarios These checks run locally in the E2E harness and are intended to be required in CI. Recommended rollout 1. Start in read-only MCP mode. 2. Validate parity and tenant-isolation tests in CI. 3. Enable write tools only after passing the full security suite. 4. Keep audit logging and alerting on denied/suspicious calls. Connect from Cursor (remote) The MCP server is deployed at https://li-nk.me/mcp using Streamable HTTP transport. No local process is needed. 1. Go to Team → MCP Keys in the LinkMe portal and generate an MCP key (works for both personal and team contexts). 2. Add or update .cursor/mcp.json at the repo root: { "mcpServers": { "linkme": { "url": "https://li-nk.me/mcp", "headers": { "Authorization": "Bearer tk_YOUR_KEY" } } } } 3. Restart Cursor (or reload the window) so the MCP server is picked up. :::caution Do not commit real API keys to version control. Either use placeholder values in the checked-in file and override locally, or add .cursor/mcp.json to .gitignore. ::: Connect from Codex (local) Use this when running MCP against your local Edge harness. 1. Build Edge once so the MCP server can import the shared policy module: npm -w apps/edge run build 2. Ensure Edge is running locally (example port 18080). 3. Add an MCP server entry in ~/.codex/config.toml: [mcp_servers."linkme-local"] command = "node" args = ["/Users/tomasradvansky/R-DEV/link-me/apps/mcp/server.mjs"] [mcp_servers."linkme-local".env] EDGE_BASE_URL = "http://127.0.0.1:18080" MCP_WRITE_ENABLED = "0" 4. Restart Codex so MCP servers reload. Optional: enable write tools Only after parity/security tests are green: [mcp_servers."linkme-local".env] EDGE_BASE_URL = "http://127.0.0.1:18080" MCP_WRITE_ENABLED = "1" Local smoke test (without Codex) You can validate protocol wiring directly from terminal: printf '%s\n' \ '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \ '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \ '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"health.get","arguments":{"auth":{"type":"session","userId":"user_demo","cookie":"YOUR_SESSION_COOKIE"}}}}' \ | EDGE_BASE_URL="http://127.0.0.1:18080" MCP_WRITE_ENABLED="0" node apps/mcp/server.mjs Related docs - API Keys & Authentication - REST Endpoints & OpenAPI - Developer Overview

Last updated on Apr 09, 2026

Link Resolution API

Resolution Note: This page is now folded into the REST Endpoints & OpenAPI doc. Use that reference for live specs, debug behaviors, and examples. GET /:slug - Detects platform from User-Agent and computes destination: - iOS: ios_custom_url → ios_store_url → deep link (https://host/<deep_link_path>) - Android: android_custom_url → android_store_url → deep link - Desktop: web_fallback_url or app-level web_fallback_url, else /d/:slug - If force_redirect_web is true and a web fallback exists, always sends users to web. - UTM: when allow_param_passthrough=1, stored utm_* values act as defaults; fields marked with overrides prefer incoming query parameters when present. - Crawler UAs: returns an OpenGraph HTML with og:* fields if not in debug mode. - Adds cid to custom URL schemes; for Play Store links adds URL-encoded referrer with cid and utm_source. - Records click and emits a redirect 302. - Override map: link.utm_override stores boolean flags (per utm_* key) indicating which fields should prefer incoming query values when merging. Debug: - GET /:slug+ renders a debug page with computed values (no redirect). GET /d/:slug - Hosted download fallback page with App Store / Play links and QR to the canonical link. GET /qr/:slug - Returns PNG when Accept includes image/png, otherwise SVG. Campaign chaining example 1. Create a marketing slug (e.g. fb-easter) with force_redirect_web=1, allow_param_passthrough=1, and a web_fallback_url pointing at https://foo.com/easter. Store baseline UTM values (utm_source=fb, utm_medium=social, utm_campaign=easter). 2. Place a download button on foo.com/easter that links to a second li-nk.me slug (e.g. download-easter) configured for deferred deep linking to the stores/apps. Leave allow_param_passthrough=1 so it merges the original UTM set with additional landing-specific keys (e.g. utm_medium=web, utm_content=landing). 3. When a visitor arrives through the campaign slug, they retain the Facebook UTM payload on the landing page. Clicking the download button propagates the original attribution and augments it with the landing metadata for downstream analytics.

Last updated on Apr 04, 2026

Webhooks API

LinkMe supports per-app webhook subscriptions in the Developer section of the portal. Webhooks are the recommended way to move LinkMe events into analytics pipelines, warehouse jobs, and operational automations without polling REST endpoints. Most teams subscribe to click and app-open events first, then add claim and deferred-claim flows once attribution QA is in place. Supported event types - link.click - link.token_created - link.claim - link.app_open - link.deferred_claim_attempt Delivery envelope { "id": "uuid", "event": "link.click", "ts": "2026-02-11T12:34:56.789Z", "app_id": "app_123", "data": { "link_id": "abc123" } } Reliability model - retriable errors are retried with backoff - retries stop after a max attempt count - every attempt is recorded in delivery history - repeated failures can auto-disable the webhook In practice, you should treat webhook receivers as at-least-once delivery consumers. Build idempotency on the id field and avoid assuming strict attempt ordering across retries. Optional signing If signing is enabled, LinkMe auto-generates a signing secret and requests include: - X-LinkMe-Signature: sha256=<hmac> Signature is HMAC-SHA256 over the raw request body. For Node backends, @li-nk.me/node-sdk exposes verifyLinkMeWebhookSignature(...) and webhook envelope parsing helpers so you can reuse core receiver logic across projects. Receiver implementation guidance Use this baseline approach in production: 1. Read the raw request body before JSON parsing. 2. Verify X-LinkMe-Signature against the raw payload. 3. Return 2xx quickly after validation. 4. Process downstream work asynchronously (queue or worker). 5. Deduplicate by event id in your sink to handle retries safely. This pattern keeps delivery healthy and prevents endpoint latency from causing avoidable retries. Typical payload handling workflow - Parse the envelope (id, event, ts, app_id, data). - Route by event type to dedicated handlers. - Enrich with campaign metadata or app/user context. - Forward to your destination system (BI, CDP, alerting, CRM). - Store processing state for replay and audit support. Portal management Use Portal -> App -> Developer -> Webhooks to: - create webhook endpoints - pick event subscriptions - set/remove signing secret - enable/disable endpoints - inspect delivery history For new integrations, begin with one endpoint and a narrow event set, validate delivery history for 24 hours, then expand subscriptions gradually. This keeps rollout risk low while still giving quick operational feedback. Related - Webhooks Guide - API Keys & Authentication

Last updated on Apr 04, 2026