Developer API · v1

Build share links into your app

Create smart app links and content links — the link behind the “share” button in your app — from your own code. A content link opens the right screen of your app when it is installed, the right store when it is not, and your web page on computers.

Base URLhttps://donelinks.com/api/v1

Overview

Every request and answer is JSON (UTF-8). Send your key in the Authorization header.

A content link per product, post or invite

Your app asks for a link when someone taps “share”; the link opens that same thing for whoever receives it.

Right store or right screen, automatically

iPhone, Android, Huawei and computers each go where they should, with your app scheme or your web page.

Safe keys for inside apps

App keys can only create and open content links for the app links you pick. Your server key stays on your server.

Counted, limited and predictable

Each answer tells you how many requests are left this month; errors say exactly what to fix.

Quick start

  1. 1
    Create a key

    Settings → Developer API: a server key for your backend, and an app key for your mobile app.

  2. 2
    Create the app link once

    With your store links and your app scheme (for example noor://). This is the address your content links live under.

  3. 3
    Make content links from the app

    Each “share” tap asks for a content link with the screen’s path. Share the URL you get back.

  4. 4
    Open them in the app

    When the app is opened by a link, call /resolve with it and open the path you get.

curl https://donelinks.com/api/v1/me \
  -H "Authorization: Bearer dl_your_server_key"

Keys and permissions

Create keys in Settings → Developer API. A key is shown once, when it is created; only a hash is stored. Revoke a key at any time and it stops at once.

Server key dl_…

For your backend only. It does what its permissions allow. Never put it in an app or a web page.

App key dl_app_…

Safe to ship inside your mobile app: it can only create and resolve content links for the app links you chose. It cannot read, change or delete anything.

Permissions of server keys

links:read
List and read your app links and their QR codes.
links:write
Create, change and delete app links (includes links:read).
content:write
Create, list, change and delete content links, and resolve them.
stats:read
Visit statistics (Stats API — plans with statistics API).

Limits

Each account has a number of API requests a month (all its keys together) and a per-minute limit, set by its plan. A batch counts one request per item. Requests refused for a wrong key, permission or plan are not counted.

PlanRequests a monthRequests a minute
Professional10,00060
Team100,000300
Team Plus250,000600
EnterpriseUnlimited1,200

Every answer carries these headers:

X-RateLimit-Limit
Requests a month on your plan, or “unlimited”.
X-RateLimit-Remaining
Requests left this month.
X-RateLimit-Reset
When the month starts again (Unix seconds).
X-Request-Id
Quote it when you contact support.

429 rate_limited: too many in a minute — wait for Retry-After seconds. 429 quota_exceeded: the month is used up — it starts again on the 1st, or upgrade.

Errors

Errors are JSON with a stable code, a readable message and, when it helps, the field to fix:

{
  "error": {
    "code": "scope_required",
    "message": "This key cannot do that. It needs the \"links:write\" permission.",
    "scope": "links:write"
  },
  "requestId": "req_9QbX2kL0aV3m"
}
CodeStatusMeaning
missing_key / invalid_key401No key, a wrong key, or a revoked key.
plan_restricted403Your plan does not include this (error.feature says which).
scope_required403The key lacks the permission (error.scope).
link_not_allowed403An app key used for an app link it was not made for.
unknown_field / invalid_field / invalid_fields400A field is unknown or not valid (error.field or error.fields).
nothing_to_open400A content link needs a path, deepLink, webUrl or params.
link_not_found / content_not_found404Not in this account.
slug_taken409This slug is used.
plan_limit_exceeded402The plan’s number of links is reached.
idempotency_mismatch422An Idempotency-Key reused with another request.
content_expired410The content link passed its expiresAt.
rate_limited / quota_exceeded429Per-minute limit or monthly requests.

Retries and pages

Safe retries (Idempotency-Key)

Send an Idempotency-Key header (up to 100 letters, digits or _-:.) when creating links or content links. If the network fails and you retry with the same key within 24 hours, you get the first result back instead of a second link (header Idempotent-Replayed: true). Errors are not stored, so a fixed request can be retried.

Pages of results

Lists answer { data, hasMore, nextCursor }. Ask for the next page with ?cursor=<nextCursor>; limit is 1 to 100 (20 by default).

App links are the DoneLinks you know from the dashboard: one link that sends every visitor to the right store. The API creates and manages app links (pages such as link-in-bio or menus stay in the dashboard). All dashboard plan rules apply: number of links, custom slugs, domains and features.

MethodPathPermissionWhat it does
GET/meanyThe key, its account and this month’s usage
GET/linkslinks:readList app links (q= search, cursor pages)
POST/linkslinks:writeCreate an app link
GET/links/{slug}links:readOne app link
PATCH/links/{slug}links:writeChange an app link (slug renames it)
DELETE/links/{slug}links:writeDelete it with its statistics and content links
GET/links/{slug}/qrlinks:readIts QR code (svg or png)
curl -X POST https://donelinks.com/api/v1/links \
  -H "Authorization: Bearer dl_your_server_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: noor-shop-app" \
  -d '{
    "slug": "noor-shop",
    "title": "Noor Shop",
    "destinations": {
      "ios": "https://apps.apple.com/app/id1234567890",
      "android": "https://play.google.com/store/apps/details?id=com.noor.shop"
    },
    "defaultUrl": "https://noor.shop",
    "appSchemes": { "ios": "noor://", "android": "noor://" }
  }'

Link fields

slug
Custom name (plans with custom links). Random when left out.
title
Your own name for the link.
destinations
{ ios, ipad, android, huawei, windows, mac, web, fallback } — store and site addresses.
defaultUrl
Where every other visitor goes.
appSchemes
{ ios, android } such as "noor://" — content links build their deep links from it.
domain
A verified custom domain of the account.
options
{ forwardParams, deferredDeepLink, desktopMode: auto | page | redirect }
campaigns
[{ id, name }] — tracked copies such as /noor-shop/instagram.
preview
{ title, description } shown when the link is shared.
qr
QR design (pattern, colors, frame, logo), as saved in the dashboard.

In PATCH send only what changes; null clears a field. Unknown fields are refused so typos never go unnoticed.

Example answer

{
  "id": "6720f1c2a9e4b1d3c5e7f901",
  "slug": "noor-shop",
  "url": "https://donelinks.com/noor-shop",
  "title": "Noor Shop",
  "domain": null,
  "destinations": {
    "ios": "https://apps.apple.com/app/id1234567890",
    "android": "https://play.google.com/store/apps/details?id=com.noor.shop"
  },
  "defaultUrl": "https://noor.shop",
  "appSchemes": { "ios": "noor://", "android": "noor://" },
  "options": { "forwardParams": false, "deferredDeepLink": false, "desktopMode": "auto" },
  "campaigns": [],
  "preview": null,
  "qr": {},
  "stats": { "clicks": 0, "qrScans": 0, "storeOpens": 0, "lastClickAt": null },
  "createdAt": "2026-10-19T09:30:00.000Z",
  "updatedAt": "2026-10-19T09:30:00.000Z"
}

QR codes

GET /links/{slug}/qr returns the link’s QR code in its own design. format=svg (default) or png, size 128–2048 (512 by default), campaign=<id> for a campaign’s code. The code opens the link with ?qr=1 so scans are counted apart.

Content links

A content link lives under an app link: https://donelinks.com/noor-shop/Xk7pQ2a, or on your domain go.noor.shop/noor-shop/Xk7pQ2a. The code is 7 letters and digits; letter case matters.

MethodPathPermissionWhat it does
POST/links/{slug}/contentcontent:write · app keysCreate a content link
POST/links/{slug}/content/batchcontent:writeCreate up to 100
GET/links/{slug}/contentlinks:read | content:writeList content links
GET/links/{slug}/content/{code}links:read | content:writeOne content link with its counts
PATCH/links/{slug}/content/{code}content:writeChange it
DELETE/links/{slug}/content/{code}content:writeDelete it
curl -X POST https://donelinks.com/api/v1/links/noor-shop/content \
  -H "Authorization: Bearer dl_app_your_app_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: share-product-123" \
  -d '{
    "path": "product/123",
    "params": { "ref": "user-42" },
    "webUrl": "https://noor.shop/p/123",
    "preview": {
      "title": "Red dress",
      "description": "Only 20 JD this week",
      "imageUrl": "https://cdn.noor.shop/p/123.jpg"
    }
  }'

Content link fields (at least one of path, deepLink, webUrl, params)

path
The screen in your app, e.g. "product/123". Added to the app link’s appSchemes → noor://product/123.
deepLink
An explicit app link instead: "noor://p/123" or { ios, android }.
params
Up to 20 text values, added to the deep links and returned by /resolve.
webUrl
The same content on the web: for computers and where the app cannot open.
preview
{ title, description, imageUrl } — what WhatsApp, Facebook, X and others show.
campaign
One of the app link’s campaign ids, to count the visits for it.
expiresAt
ISO date; after it the link answers 410 (offers, invites).

What a content link does

Android
Opens your app on the content when it is installed (Chrome “intent”), otherwise Google Play. With deferred deep linking on your plan, Google Play passes the link to the app after the install.
iPhone and iPad
With Universal Links on your domain, iOS opens your app directly. Otherwise: the App Store.
Computers
The content’s webUrl; without one, what the app link gives computers (its website or the app page with a QR code of this content).
Chat apps
WhatsApp, Telegram, Facebook, X, LinkedIn… show the content’s own preview.

Content links follow their app link: its password and visit cap, blocking, store links, country routing and statistics. During maintenance mode they keep working.

Example answer

{
  "code": "Xk7pQ2a",
  "url": "https://donelinks.com/noor-shop/Xk7pQ2a",
  "link": "noor-shop",
  "path": "product/123",
  "deepLink": { "ios": "noor://product/123?ref=user-42", "android": "noor://product/123?ref=user-42" },
  "params": { "ref": "user-42" },
  "webUrl": "https://noor.shop/p/123",
  "preview": { "title": "Red dress", "description": "Only 20 JD this week", "imageUrl": "https://cdn.noor.shop/p/123.jpg" },
  "campaign": null,
  "expiresAt": null,
  "stats": { "clicks": 0, "appOpens": 0, "lastClickAt": null },
  "createdAt": "2026-10-19T09:31:00.000Z",
  "updatedAt": "2026-10-19T09:31:00.000Z"
}

POST /links/{slug}/content/batch creates up to 100 at once. All or nothing: if one item is not valid nothing is created and error.index says which.

curl -X POST https://donelinks.com/api/v1/links/noor-shop/content/batch \
  -H "Authorization: Bearer dl_your_server_key" \
  -H "Content-Type: application/json" \
  -d '{ "items": [
    { "path": "product/1", "webUrl": "https://noor.shop/p/1" },
    { "path": "product/2", "webUrl": "https://noor.shop/p/2" },
    { "path": "product/3", "campaign": "instagram" }
  ] }'

Resolve in the app

When a DoneLink opens your app — through Universal Links / App Links on your domain, or right after an install from Google Play — send that link to /resolve and open what it returns. It also counts the open in the app.

MethodPathPermissionWhat it does
GET/resolve?url=…content:resolve · app keysWhat a link should open in the app
curl -G https://donelinks.com/api/v1/resolve \
  -H "Authorization: Bearer dl_app_your_app_key" \
  --data-urlencode "url=https://go.noor.shop/noor-shop/Xk7pQ2a"

Honest note: on iPhone, opening the content right after a first install is not possible without an SDK inside the app (Apple does not pass the link through the App Store). DoneLinks does not use fingerprinting. The app opens normally; links tapped after that open the content.

Share button guide

  1. 1
    Create the app link (once)

    With your store links and app scheme. If you have a custom domain, put the link on it: Universal Links need your own domain.

  2. 2
    In the app: on “share”

    Ask for a content link with the screen’s path and a preview, then share the URL.

  3. 3
    In the app: when opened by a link

    Send the link to /resolve and open the returned path.

  4. 4
    Android after an install

    Read the Install Referrer once and resolve it (deferred deep linking).

Universal Links (iOS) and App Links (Android)

For links to open your app directly, the app must trust your domain. Add your domain in Settings → Custom domains, then in “Open in app” upload these two files. DoneLinks serves them on your domain.

{
  "applinks": {
    "details": [
      {
        "appIDs": ["TEAMID.com.noor.shop"],
        "components": [{ "/": "/noor-shop/*" }]
      }
    ]
  }
}
[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.noor.shop",
    "sha256_cert_fingerprints": ["AB:CD:EF:…"]
  }
}]
<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="https" android:host="go.noor.shop" android:pathPrefix="/noor-shop/" />
</intent-filter>

Statistics

GET /stats is the Stats API: visits per day, device, country, referrer and UTM for each link (period=yesterday | 30d | 400d, link=<slug>, format=csv). It needs the stats:read permission and a plan with statistics API. Keys made for it before keep working.

MethodPathPermissionWhat it does
GET/statsstats:readVisit statistics (Stats API)

From Firebase Dynamic Links

Firebase Dynamic Links shut down in August 2025. Here is how its pieces map to DoneLinks:

Dynamic Link domain (xyz.page.link)Your domain (go.brand.com) or https://donelinks.com
Long link parameters ibi, isi, apn, afl, iflApp link: destinations and appSchemes
link= (the deep link)Content link: path or deepLink, and webUrl
Social tags st, sd, siContent link: preview { title, description, imageUrl }
REST API shortLinksPOST /links/{slug}/content
getDynamicLink / handleUniversalLinkGET /resolve
Deferred deep linksAndroid: yes (Install Referrer). iOS: no (see the note above).

OpenAPI and tools

The whole API is described in OpenAPI 3.1: import it into Postman or Insomnia, generate a client, or give it to an AI assistant.

OpenAPI 3.1https://donelinks.com/api/v1/openapi.json

Postman: Import → Link → paste the OpenAPI address.