API — Links
Create, manage, and track short deep links. Supports single creation, bulk operations, QR codes, and templates.
/api/v1/* routes require the header X-Api-Key: <API_SECRET> (or Authorization: Bearer <jwt>). If API_SECRET is not set in .env, auth is disabled.Create a Link
{
"slug": "summer-sale", // optional — auto-generated if absent
"title": "Summer Sale",
"description": "20% off everything", // optional
"image_url": "https://example.com/og.jpg", // optional, OG image
"ios_uri_scheme": "myapp://product/42",
"ios_store_url": "https://apps.apple.com/app/id123",
"android_uri_scheme": "myapp://product/42",
"android_store_url": "https://play.google.com/store/apps/details?id=com.ex",
"web_url": "https://example.com/product/42",
"campaign": "summer_2025",
"source": "instagram",
"medium": "story",
"custom_data": { "product_id": 42, "screen": "tab" },
"expires_at": "2025-09-01T00:00:00Z", // optional — ISO 8601
"max_clicks": 1000, // optional — click cap
"targeting_rules": {
"allow_devices": ["ios", "android"],
"allow_countries": ["FR", "BE"],
"block_countries": ["CN"]
}
}
Response (201):
{
"id": 42,
"slug": "summer-sale",
"short_url": "https://links.yourapp.com/summer-sale",
"title": "Summer Sale",
// ... all link fields
}
At least one destination (ios_uri_scheme, android_uri_scheme, or web_url) is required.
Bulk Create
{
"links": [
{ "title": "Link 1", "web_url": "https://example.com/1" },
{ "title": "Link 2", "web_url": "https://example.com/2" }
]
}
Response (207):
{
"total": 100, "created": 97, "failed": 3,
"links": [
{ "index": 0, "slug": "abc123", "short_url": "https://links.yourapp.com/abc123" }
],
"errors": [
{ "index": 12, "error": "Slug already taken" }
]
}
Partial success is possible. Each item has its own index for correlation. Maximum 500 links per call.
Snippets — bulk create
Recommandé : ajouter Idempotency-Key sur les requêtes bulk pour rendre le retry safe (cf. idempotency).
# 100 liens depuis un fichier JSON local
curl -X POST "$BASE/api/v1/links/bulk" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
--data @bulk-payload.json
Node / JS
import { randomUUID } from 'node:crypto';
const r = await fetch(`${BASE}/api/v1/links/bulk`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': randomUUID(),
},
body: JSON.stringify({ links }), // links: array of ≤500 objects
});
const { total, created, failed, errors } = await r.json();
if (failed) console.warn('errors', errors);
Python
import requests, uuid
resp = requests.post(
f"{BASE}/api/v1/links/bulk",
headers={
"Authorization": f"Bearer {API_KEY}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={"links": links}, # list of dicts, ≤500
timeout=30,
)
data = resp.json()
if data["failed"]:
for e in data["errors"]:
print(f"link[{e['index']}]: {e['error']}")
FDL importer (Firebase Dynamic Links migration)
Endpoint dédié POST /api/v1/tools/fdl-import pour migrer les liens depuis un export Firebase Dynamic Links (FDL est deprecated par Google en 2025). Accepte un array brut ou un objet enveloppe {shortLinks: [...]} ou {links: [...]}.
# cURL — depuis un export FDL
curl -X POST "$BASE/api/v1/tools/fdl-import" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
--data @firebase-export.json
# Réponse :
# { "imported": 247, "skipped": 12, "errors": [{...}] }
L'import dédupliue par slug. Les liens FDL utilisant des paramètres
analytics Firebase (utm_source, etc.) sont mappés sur
links.source/medium/campaign.
Cf. tools §FDL importer pour
l'endpoint complet.
List Links
| Parameter | Default | Description |
|---|---|---|
page | 1 | Page number |
limit | 50 | Items per page (max 200) |
search | — | Filter by slug, title, or campaign |
active | — | 1 or 0 to filter by active status |
Get a Link
Returns the full link object including analytics counters (total_clicks, total_installs).
Update a Link
All fields are optional — only send what you want to update.
{
"title": "Updated Title",
"active": 0,
"max_clicks": 5000
}
Delete a Link
Returns 204 No Content on success. This operation is irreversible — all analytics data for the link is also deleted.
QR Code
Generates on-the-fly (no storage). The QR code encodes the short URL.
| Parameter | Default | Description |
|---|---|---|
format | svg | svg (vector) or png (raster) |
size | 400 | Size in pixels (64–2048) |
GET /api/v1/links/summer-sale/qr?format=svg&size=400
GET /api/v1/links/summer-sale/qr?format=png&size=800 ← for print
Response: binary image with Content-Type: image/svg+xml or image/png.
Link Controls
Expiration (expires_at)
ISO 8601 date after which the link is inactive. Returns HTTP 410 Gone. Useful for limited-time offers.
"expires_at": "2025-09-01T00:00:00Z"
Click Cap (max_clicks)
Maximum number of allowed clicks. Returns HTTP 410 Gone once the cap is reached. Ideal for limited-use promo codes.
"max_clicks": 1000
Targeting Rules
The targeting_rules field restricts link access by platform and country. Geolocation uses geoip-lite (local MaxMind database — no external network calls).
"targeting_rules": {
"allow_devices": ["ios", "android"], // empty = all devices
"allow_countries": ["FR", "BE", "CH"], // whitelist — empty = all countries
"block_countries": ["CN", "RU"] // blacklist
}
| Rule | Behavior when violated |
|---|---|
allow_devices | HTTP 403 if device is not in the list |
allow_countries | HTTP 403 if country is not in the whitelist |
block_countries | HTTP 403 if country is in the blacklist |
Public Redirect — Response Codes
| Code | Cause |
|---|---|
200 | Redirect HTML page served |
403 | Country or device targeting rule violated |
404 | Slug does not exist or link is disabled |
410 | Link expired or click cap reached |
Postman collection
Une collection Postman officielle est versionnée dans le repo et servie publiquement pour démarrer rapidement avec l'API. Tous les endpoints principaux sont pré-configurés avec variables d'environnement.
Téléchargement
- Lien direct :
/postman-collection.json(≈8 KB). - Source de vérité :
public/postman-collection.jsondansakeeli-tech/deeplink-server. - Schéma : Postman Collection v2.1 (compatible Postman 7.0+, Insomnia, Bruno).
Run in Postman
Importez la collection dans Postman :
- Ouvrir Postman → Import → coller l'URL
https://<votre-domaine>/postman-collection.jsonOU drag-drop le fichier téléchargé. - Postman crée automatiquement l'environnement avec les variables prédéfinies.
- Configurer les variables (cf. ci-dessous) puis lancer Auth → Login pour récupérer un
accessTokenJWT, ou utiliser uneapiKeytenantdlk_*.
Variables d'environnement
| Variable | Default | Description |
|---|---|---|
baseUrl | https://deeplink-server-...run.app | URL racine de votre déploiement, sans / final. À remplacer par votre URL self-hosted. |
accessToken | (vide) | JWT obtenu via Auth → Login. Remplit automatiquement après login si vous utilisez les Tests Postman. |
apiKey | (vide) | Clé tenant dlk_*. Alternative au JWT pour scripts/CI. |
tenantSlug | main | Slug du tenant cible. Visible dans l'URL dashboard. |
linkSlug | demo-link | Slug du lien utilisé dans les requêtes Get / Update / Delete. |
L'auth Bearer est configurée au niveau collection avec {{accessToken}} par défaut.
Pour utiliser apiKey, surcharger l'auth au niveau du dossier ou de la requête.
Tutorial — premier appel en 3 étapes
-
Importer la collection (cf. ci-dessus). Vérifier que l'environnement
DeepLink Serverapparaît en haut à droite. -
Obtenir un token : ouvrir Auth → Login (local), ajuster le body avec votre
email/password/tenantSlug, lancer la requête. Copier leaccessTokende la réponse dans la variable d'environnementaccessToken. -
Premier appel : ouvrir Links → List Links, lancer. Vous devriez voir vos liens en JSON. Si
401, le token est expiré → relancer Auth → Refresh (ou Login).
collection.info.version et la date du fichier
par rapport à votre version de serveur. Si décalage, ouvrir une issue label:doc.
Link Templates
Templates let you save preset link configurations to quickly create new links with shared settings.
{
"name": "iOS Campaign Template",
"description": "Standard iOS campaign link",
"preset": {
"ios_uri_scheme": "myapp://campaign",
"ios_store_url": "https://apps.apple.com/app/id123",
"campaign": "ios_2025"
}
}
The preset object contains the default values applied when creating a link from this template.
Returns 204 No Content. Existing links created from this template are not affected.