Documentation API

L'API Hou.la vous permet d'intégrer la création et la gestion de liens courts directement dans vos applications, scripts ou services.

Base URL :https://hou.la/api

1. Authentification

Toutes les requêtes à l'API doivent être authentifiées en utilisant une clé API. Vous pouvez créer et gérer vos clés API depuis votre espace Manager.

Incluez votre clé API dans l'en-tête X-API-Key de chaque requête :

X-API-Key: houla_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Important : Ne partagez jamais votre clé API. Elle donne accès à votre compte et à toutes vos données.

Workspaces (multi-comptes)

Toutes les ressources (liens, tags, webhooks, domaines, etc.) sont rattachées à un workspace et non directement à un compte utilisateur. Chaque utilisateur dispose d'un workspace personnel créé automatiquement à l'inscription.

Pour cibler un workspace spécifique, ajoutez l'en-tête X-Workspace-Id à vos requêtes :

X-Workspace-Id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Si cet en-tête est omis, le workspace personnel par défaut de l'utilisateur est utilisé automatiquement.

Bon à savoir : Les clés API sont liées à un workspace. Les requêtes effectuées avec une clé API ciblent automatiquement le workspace associé, sans besoin de l'en-tête X-Workspace-Id.

Exemple avec workspace

curl -X POST "https://hou.la/api/link" \
  -H "X-API-Key: houla_sk_xxxxxxxx" \
  -H "X-Workspace-Id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/page"
  }'

2. Créer un lien court

PAGES.API_DOC.CREATE_AUTH_NOTE_TITLE PAGES.API_DOC.CREATE_AUTH_NOTE_DESC

POST/link

Paramètres

ParamètreTypeRequisDescription
urlstringOuiL'URL longue à raccourcir
titlestringNonUn titre pour identifier le lien
keystringNonUne clé personnalisée (ex: "mon-lien")
utm_sourcestringNonSource de la campagne (ex: "google", "newsletter", "facebook")
utm_mediumstringNonSupport marketing (ex: "cpc", "email", "social")
utm_campaignstringNonNom de la campagne (ex: "soldes-ete-2026")
utm_termstringNonMots-clés payés (pour les campagnes publicitaires)
utm_contentstringNonContenu spécifique (ex: "bouton-header", "lien-footer")
utm_idstringNonIdentifiant de campagne pour le suivi avancé
ephemeralDurationstringNonDurée de vie du lien éphémère. Valeurs : 1h, 6h, 12h, 24h, 48h
customExpiresAtstringNonDate d'expiration personnalisée (ISO 8601, ex: 2026-06-15T23:59:59.000Z). Mutuellement exclusif avec ephemeralDuration. La clé n'est pas recyclée.
passwordstringNonMot de passe pour protéger l'accès au lien (1-100 caractères). Hashé avec bcrypt. Sécurité renforcée.
fbPixelIdstringNonFacebook Pixel ID (10-20 chiffres, ex: 123456789012345). Le pixel se déclenche lors du clic pour constituer une audience de retargeting.
googleTagIdstringNonGoogle Tag ID (format G-XXX, AW-XXX, DC-XXX ou UA-XXX). Supporte gtag.js.
tiktokPixelIdstringNonTikTok Pixel ID (format CXXX..., ex: CABCDEF1234567890). Le pixel se déclenche lors du clic pour le suivi TikTok Ads.
ogTitlestringNonTitre Open Graph personnalisé affiché lors du partage sur les réseaux sociaux (max 200 caractères)
ogDescriptionstringNonDescription Open Graph personnalisée affichée lors du partage (max 500 caractères)
ogImageUrlstringNonURL de l'image Open Graph (doit être une URL valide commençant par http:// ou https://)
tagIdsstring[]NonListe d'identifiants de tags à associer au lien (UUID). Les tags doivent exister au préalable.
customDomainIdstringNonPAGES.API_DOC.CREATE_CUSTOM_DOMAIN_ID_DESC
maxHitsnumberNonNombre maximum de clics autorisés sur le lien (entre 1 et 1 000 000). Une fois atteint, le lien expire automatiquement.
isCloakedbooleanNonActiver le Link Cloaking (<code class="code-inline">true</code>/<code class="code-inline">false</code>). Masque l'URL de destination dans la barre d'adresse du visiteur.
deepLinkIosstringNonSchéma URI iOS pour ouvrir l'app native (ex: instagram://user?username=monprofil). Mobile uniquement.
deepLinkAndroidstringNonIntent URL Android pour ouvrir l'app native (ex: intent://user?username=monprofil#Intent;scheme=instagram;package=com.instagram.android;end). Mobile uniquement.
deepLinkFallbackUrlstringNonURL de repli si l'app n'est pas installée (ex: lien App Store, Play Store ou page web). Si non renseigné, la redirection standard s'applique.
Variables UTM : Ces paramètres sont automatiquement ajoutés à l'URL de destination pour le suivi dans Google Analytics et autres outils d'analyse.
Liens éphémères :Si vous spécifiez ephemeralDuration, le lien expirera automatiquement après la durée indiquée. La clé sera alors recyclée et pourra être réutilisée.
Expiration personnalisée :Si vous spécifiez customExpiresAt (ISO 8601), le lien expirera à cette date précise. La clé n'est pas recyclée. Mutuellement exclusif avec ephemeralDuration.
Liens protégés :Si vous spécifiez password, le visiteur devra saisir le mot de passe avant d'être redirigé. Le mot de passe est hashé avec bcrypt (jamais stocké en clair). Sécurité renforcée.
Retargeting Pixels : Ajoutez vos pixels Facebook, Google ou TikTok pour constituer des audiences de reciblage. Les pixels se déclenchent sur une page intermédiaire lors du clic (invisible pour les bots/crawlers, qui reçoivent un 301 direct).

Exemple de requête

curl -X POST "https://hou.la/api/link" \
  -H "X-API-Key: houla_sk_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/ma-page-tres-longue",
    "title": "Ma campagne marketing",
    "utm_source": "newsletter",
    "utm_medium": "email",
    "utm_campaign": "promo-janvier-2026"
  }'

Exemple avec lien éphémère

curl -X POST "https://hou.la/api/link" \
  -H "X-API-Key: houla_sk_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/offre-limitee",
    "title": "Promo flash 24h",
    "ephemeralDuration": "24h"
  }'

Exemple avec expiration personnalisée

curl -X POST "https://hou.la/api/link" \
  -H "X-API-Key: houla_sk_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/evenement",
    "title": "Inscription ouverte jusqu'au 15 juin",
    "customExpiresAt": "2026-06-15T23:59:59.000Z"
  }'

Exemple avec lien protégé par mot de passe

curl -X POST "https://hou.la/api/link" \
  -H "X-API-Key: houla_sk_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/document-confidentiel",
    "title": "Document privé",
    "password": "secret123"
  }'

Réponse

201 Created

{
  "id": "abc123",
  "key": "xY7k9",
  "url": "https://example.com/ma-page-tres-longue",
  "shortUrl": "https://hou.la/xY7k9",
  "title": "Ma campagne marketing",
  "hasPassword": false,
  "isEphemeral": false,
  "hitsCount": 0,
  "flashsCount": 0,
  "createdAt": "2026-01-28T10:30:00Z",
  "status": "active"
}

Réponse (lien éphémère)

{
  "id": "def456",
  "key": "aB3cD",
  "url": "https://example.com/offre-limitee",
  "shortUrl": "https://hou.la/aB3cD",
  "title": "Promo flash 24h",
  "hasPassword": false,
  "isEphemeral": true,
  "ephemeralDuration": "24h",
  "expiresAt": "2026-01-29T10:30:00Z",
  "createdAt": "2026-01-28T10:30:00Z",
  "status": "active"
}

Réponse (expiration personnalisée)

{
  "id": "jkl012",
  "key": "mN4pQ",
  "url": "https://example.com/evenement",
  "shortUrl": "https://hou.la/mN4pQ",
  "title": "Inscription ouverte jusqu'au 15 juin",
  "hasPassword": false,
  "isEphemeral": false,
  "expiresAt": "2026-06-15T23:59:59.000Z",
  "createdAt": "2026-02-12T10:30:00Z",
  "status": "active"
}

Réponse (lien protégé)

{
  "id": "ghi789",
  "key": "zK8mP",
  "url": "https://example.com/document-confidentiel",
  "shortUrl": "https://hou.la/zK8mP",
  "title": "Document privé",
  "hasPassword": true,
  "isEphemeral": false,
  "hitsCount": 0,
  "flashsCount": 0,
  "createdAt": "2026-01-28T10:30:00Z",
  "status": "active"
}
QR Code :Pour générer un QR code, utilisez l'endpoint dédié GET /api/link/{id}/qrcode (voir section 5).

3. Vérifier la disponibilité d'une clé

GET/link/{key}/availability

Endpoint public : Aucune authentification requise.

Exemple de requête

curl -X GET "https://hou.la/api/link/mon-lien/availability"

Réponse

200 OK

{
  "available": true
}

4. Récupérer un lien

GET/link/{id}

Récupère les détails complets d'un lien par son UUID, incluant les statistiques des 90 derniers jours et les tags.

Exemple de requête

curl -X GET "https://hou.la/api/link/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "X-API-Key: houla_sk_xxxxxxxx"

Réponse

200 OK

{
  "id": "abc123",
  "key": "xY7k9",
  "url": "https://example.com/ma-page-tres-longue",
  "shortUrl": "https://hou.la/xY7k9",
  "title": "Ma campagne marketing",
  "hasPassword": false,
  "isEphemeral": false,
  "hitsCount": 42,
  "flashsCount": 5,
  "createdAt": "2026-01-28T10:30:00Z",
  "status": "active"
}

5. Récupérer le QR code d'un lien

GET/link/{id}/qrcode

Paramètres de requête

ParamètreTypeDéfautDescription
widthnumber300Largeur en pixels (50-1000)
marginnumber2Marge autour du QR code (0-10)
darkColorstring#000000Couleur des modules (hex)
lightColorstring#FFFFFFCouleur de fond (hex)
errorCorrectionLevelstringMNiveau de correction : L, M, Q, H
formatstringpngFormat de sortie : png ou svg

Exemple de requête

curl -X GET "https://hou.la/api/link/a1b2c3d4-e5f6-7890-abcd-ef1234567890/qrcode?width=500&darkColor=%23FF5722" \
  -H "X-API-Key: houla_sk_xxxxxxxx"

Réponse (PNG)

200 OK

{
  "base64": "iVBORw0KGgoAAAANSUhEUgAA...",
  "dataUrl": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
}

Réponse (SVG)

{
  "svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 300 300\">...</svg>"
}

6. Lister vos liens

GET/link

Paramètres de requête

ParamètreTypeDéfautDescription
pagenumber1Numéro de la page
limitnumber20Nombre de résultats par page (max: 100)

Exemple de requête

curl -X GET "https://hou.la/api/link?page=1&limit=10" \
  -H "X-API-Key: houla_sk_xxxxxxxx"

Réponse

200 OK

{
  "data": [
    {
      "id": "abc123",
      "key": "xY7k9",
      "url": "https://example.com/page1",
      "shortUrl": "https://hou.la/xY7k9",
      "title": "Lien 1",
      "hasPassword": false,
      "isEphemeral": false,
      "hitsCount": 42,
      "createdAt": "2026-01-28T10:30:00Z"
    },
    ...
  ],
  "total": 55,
  "page": 1,
  "pageCount": 6,
  "count": 10
}
PAGES.API_DOC.LIST_SORT_TITLE PAGES.API_DOC.LIST_SORT_DESC

7. Modifier un lien

PATCH/link/{id}

Paramètres

ParamètreTypeDescription
urlstringLa nouvelle URL de destination
titlestringLe nouveau titre du lien
passwordstringNouveau mot de passe (1-100 caractères), ou null pour retirer la protection
isEphemeralbooleanActiver/désactiver le mode éphémère
ephemeralDurationstringDurée éphémère : 1h, 6h, 12h, 24h, 48h
customExpiresAtstringDate d'expiration personnalisée (ISO 8601), ou null pour retirer l'expiration
fbPixelIdstringFacebook Pixel ID (10-20 chiffres), ou null pour retirer le pixel
googleTagIdstringGoogle Tag ID (G-XXX, AW-XXX, DC-XXX, UA-XXX), ou null pour retirer
tiktokPixelIdstringTikTok Pixel ID (CXXX...), ou null pour retirer
ogTitlestring | nullNouveau titre Open Graph, ou null pour retirer
ogDescriptionstring | nullNouvelle description Open Graph, ou null pour retirer
ogImageUrlstring | nullNouvelle URL d'image Open Graph, ou null pour retirer
tagIdsstring[] | nullListe d'identifiants de tags (UUID) à associer au lien, ou null pour retirer tous les tags
maxHitsnumber | nullNombre maximum de clics autorisés (1 à 1 000 000), ou null pour retirer la limite
isCloakedbooleanActiver ou désactiver le Link Cloaking (true/false)
deepLinkIosstring | nullSchéma URI iOS, ou null pour supprimer
deepLinkAndroidstring | nullIntent URL Android, ou null pour supprimer
deepLinkFallbackUrlstring | nullURL de repli, ou <code class="code-inline">null</code> pour supprimer

Exemple de requête

curl -X PATCH "https://hou.la/api/link/abc123" \
  -H "X-API-Key: houla_sk_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Nouveau titre",
    "url": "https://example.com/nouvelle-destination"
  }'

Réponse

200 OK

{
  "id": "abc123",
  "key": "xY7k9",
  "url": "https://example.com/nouvelle-destination",
  "shortUrl": "https://hou.la/xY7k9",
  "title": "Nouveau titre",
  "hasPassword": false,
  "updatedAt": "2026-01-28T14:00:00Z"
}

8. Supprimer un lien

DELETE/link/{id}

Supprime un lien (suppression logique). La clé peut être libérée pour réutilisation.

PAGES.API_DOC.DELETE_NO_BODY_NOTE

Exemple de requête

curl -X DELETE "https://hou.la/api/link/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "X-API-Key: houla_sk_xxxxxxxx"

Réponse

200 OK

{
  "success": true,
  "message": "Lien supprimé avec succès"
}
PAGES.API_DOC.DELETE_MANAGER_TITLEPAGES.API_DOC.DELETE_MANAGER_DESC

9. Upload d’image OG

Uploadez une image personnalisée qui sera optimisée (1200×630, WebP) et stockée sur notre CDN. L'image remplace automatiquement le champ ogImageUrl du lien.

Uploader une image

POST/manager/link/{id}/og-image

Le body doit être de type <code class="code-inline">multipart/form-data</code> avec un champ <code class="code-inline">file</code> (JPG, PNG ou WebP, max 8 Mo).

Exemple de requête

curl -X POST "https://hou.la/api/manager/link/abc123/og-image" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "file=@my-og-image.jpg"

Réponse

200 OK

{
  "ogImageUrl": "https://r2.hou.la/og-images/user123/link456/abc.webp",
  "ogImageR2Key": "og-images/user123/link456/abc.webp"
}

Supprimer l'image uploadée

DELETE/manager/link/{id}/og-image

curl -X DELETE "https://hou.la/api/manager/link/abc123/og-image" \
  -H "Authorization: Bearer YOUR_TOKEN"

204 No Content

Crawler les meta OG

POST/manager/link/{id}/og-crawl

Re-crawle les meta Open Graph depuis l’URL de destination du lien. Utile pour mettre à jour les données OG après modification de la page cible.

curl -X POST "https://hou.la/api/manager/link/abc123/og-crawl" \
  -H "Authorization: Bearer YOUR_TOKEN"

Réponse

200 OK

{
  "ogTitle": "Page Title",
  "ogDescription": "Page description from meta tags",
  "ogImageUrl": "https://example.com/og-image.jpg",
  "ogCrawlStatus": "success",
  "ogCrawledAt": "2026-02-16T10:30:00.000Z"
}

Prévisualiser les meta OG d’une URL

POST/manager/link/og-crawl-url

Crawle une URL sans créer de lien, pour prévisualiser les meta OG qui seront extraites.

ParamètreTypeRequisDescription
urlstringOuiURL à crawler (ex : https://example.com)
curl -X POST "https://hou.la/api/manager/link/og-crawl-url" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/my-page"
  }'

Réponse

200 OK

{
  "ogTitle": "My Page Title",
  "ogDescription": "Description from meta tags",
  "ogImageUrl": "https://example.com/image.jpg"
}

Récupérer les données OG d’un lien

GET/manager/link/{id}/og-preview

Retourne les meta OG stockées pour un lien (titre, description, image, statut du crawl).

curl -X GET "https://hou.la/api/manager/link/abc123/og-preview" \
  -H "Authorization: Bearer YOUR_TOKEN"

Réponse

200 OK

{
  "ogTitle": "Page Title",
  "ogDescription": "Page description",
  "ogImageUrl": "https://r2.hou.la/og-images/user/link/abc.webp",
  "ogCrawlStatus": "manual",
  "ogCrawledAt": "2026-02-16T10:30:00.000Z"
}
Auto-détection : Si aucun champ OG n’est fourni à la création, le système crawle automatiquement l’URL de destination pour extraire les meta OG existantes (og:title, og:description, og:image avec fallbacks sur twitter:title, meta description, etc.).
Bots sociaux : Lorsqu’un bot social (facebookexternalhit, Twitterbot, LinkedInBot, WhatsApp, Telegram, Discord, etc.) accède au lien court, il reçoit une page HTML avec les meta OG au lieu d’une redirection 301. Les visiteurs humains sont toujours redirigés normalement.

10. Smart Routing (règles de redirection)

Le Smart Routing permet de définir des règles de redirection conditionnelle sur vos liens. Chaque règle contient un libellé, une URL de destination, des conditions (pays, appareil, langue, navigateur, OS, première visite) et un poids optionnel pour l'A/B testing.

Lister les règles d'un lien

GET/link/{linkId}/rules

curl -X GET "https://hou.la/api/link/abc123/rules" \
  -H "X-API-Key: houla_sk_xxxxxxxx"

Réponse

200 OK

[
  {
    "id": "rule-1",
    "linkId": "abc123",
    "label": "Visiteurs français mobile",
    "destinationUrl": "https://example.fr/page-fr",
    "matchType": "all",
    "priority": 0,
    "weight": 100,
    "isActive": true,
    "conditions": [
      {
        "field": "country",
        "operator": "equals",
        "value": "FR"
      }
    ]
  }
]

Créer une règle

POST/link/{linkId}/rules

Paramètres

ParamètreTypeRequisDescription
labelstringOuiLibellé de la règle (max 100 caractères)
destinationUrlstringOuiURL de destination si la règle correspond
matchTypestringNonall (défaut) = toutes les conditions, any = au moins une
weightnumberNonPoids pour l'A/B testing (0-100). Si des règles ont des poids, le trafic est réparti proportionnellement.
isActivebooleanNonActiver/désactiver la règle (défaut: true)
conditionsarrayOuiListe de conditions (voir ci-dessous)

Structure d'une condition

ParamètreTypeValeurs possibles
fieldstringcountry, continent, device, os, browser, language, referrer, social_media, day_of_week, hour, date_range, is_bot, is_first_visit
operatorstringequals, not_equals, contains, not_contains, in, not_in, starts_with, greater_than, less_than, between, not_between
valuestringValeur à comparer (ex: FR, mobile, true)

Exemple de requête

curl -X POST "https://hou.la/api/link/abc123/rules" \
  -H "X-API-Key: houla_sk_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Visiteurs français mobile",
    "destinationUrl": "https://example.fr/page-fr",
    "matchType": "all",
    "conditions": [
      { "field": "country", "operator": "equals", "value": "FR" },
      { "field": "device", "operator": "equals", "value": "mobile" }
    ]
  }'

A/B Testing avec poids

Pour répartir le trafic entre plusieurs destinations, créez des règles sans conditions mais avec des poids. Le trafic sera distribué proportionnellement aux poids définis.

# Règle A - 70% du trafic
curl -X POST "https://hou.la/api/link/abc123/rules" \
  -H "X-API-Key: houla_sk_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Variante A",
    "destinationUrl": "https://example.com/page-a",
    "weight": 70,
    "conditions": []
  }'

# Règle B - 30% du trafic
curl -X POST "https://hou.la/api/link/abc123/rules" \
  -H "X-API-Key: houla_sk_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Variante B",
    "destinationUrl": "https://example.com/page-b",
    "weight": 30,
    "conditions": []
  }'

Modifier une règle

PATCH/link/{linkId}/rules/{ruleId}

Mêmes paramètres que la création. Seuls les champs envoyés sont modifiés.

Supprimer une règle

DELETE/link/{linkId}/rules/{ruleId}

Réordonner les règles

PUT/link/{linkId}/rules/reorder

curl -X PUT "https://hou.la/api/link/abc123/rules/reorder" \
  -H "X-API-Key: houla_sk_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "ruleIds": ["rule-2", "rule-1", "rule-3"]
  }'

11. Webhooks

Les webhooks vous permettent de recevoir des notifications HTTP en temps réel lorsque des événements se produisent sur vos liens ou pages Link-in-Bio. Au lieu de poll l'API régulièrement, configurez un webhook et recevez les données directement sur votre serveur.

PAGES.API_DOC.WEBHOOKS_AUTH_TITLE PAGES.API_DOC.WEBHOOKS_AUTH_DESC
Exclusivité hou.la :Nous sommes les seuls à proposer les événements profile.visited et profile.link_clicked pour les pages Link-in-Bio.

Événements disponibles

ÉvénementDescription
link.clickedUn lien a été cliqué
link.createdUn lien a été créé
link.updatedUn lien a été modifié
link.deletedUn lien a été supprimé
link.health_changedL'état de santé d'un lien a changé (ex: URL cassée)
link.safety_changedLe statut de sécurité d'un lien a changé
link.expiredUn lien éphémère a expiré
link.password_attemptTentative d'accès à un lien protégé par mot de passe
profile.visitedUne page Link-in-Bio a été visitée
profile.link_clickedUn lien a été cliqué sur une page Link-in-Bio

Créer un webhook

POST/manager/webhook

Paramètres

ParamètreTypeRequisDescription
namestringOuiNom descriptif (max 100 caractères)
urlstringOuiURL de destination (HTTPS requis en production, max 2048 caractères)
eventsstring[]OuiListe des événements à écouter (au moins 1)
linkIdstringNonUUID du lien spécifique à surveiller
tagIdstringNonUUID du tag pour filtrer les événements
batchSizenumberNonTaille du batch (1-100, défaut: 1 = pas de batching)
batchDelayMsnumberNonDélai max avant envoi du batch en ms (0-60000, défaut: 0)
samplingRatenumberNonPourcentage d'événements à envoyer (1-100, défaut: 100)
anonymizeIpbooleanNonAnonymiser l'IP dans les payloads (RGPD)
excludeGeoCitybooleanNonExclure la ville des données de géolocalisation (RGPD)

Exemple de requête

curl -X POST "https://hou.la/api/manager/webhook" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Analytics Integration",
    "url": "https://my-app.com/webhooks/houla",
    "events": ["link.clicked", "link.created"],
    "anonymizeIp": true,
    "excludeGeoCity": true
  }'

Réponse

201 Created

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "name": "Analytics Integration",
  "url": "https://my-app.com/webhooks/houla",
  "events": ["link.clicked", "link.created"],
  "enabled": true,
  "secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxx",
  "anonymizeIp": true,
  "excludeGeoCity": true,
  "batchSize": 1,
  "samplingRate": 100,
  "createdAt": "2025-01-15T10:30:00.000Z"
}
Important :Le secret n'est affiché qu'à la création. Conservez-le en lieu sûr pour vérifier les signatures.

Lister les webhooks

GET/manager/webhook

curl -X GET "https://hou.la/api/manager/webhook" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Récupérer un webhook

GET/manager/webhook/{id}

curl -X GET "https://hou.la/api/manager/webhook/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Modifier un webhook

PATCH/manager/webhook/{id}

Tous les champs de création sont acceptés en paramètre (sauf secret).

curl -X PATCH "https://hou.la/api/manager/webhook/a1b2c3d4-..." \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Updated Integration",
    "events": ["link.clicked", "link.created", "link.deleted"]
  }'

Supprimer un webhook

DELETE/manager/webhook/{id}

curl -X DELETE "https://hou.la/api/manager/webhook/a1b2c3d4-..." \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

204 No Content

Activer / Désactiver

POST/manager/webhook/{id}/enable

POST/manager/webhook/{id}/disable

curl -X POST "https://hou.la/api/manager/webhook/a1b2c3d4-.../enable" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Tester un webhook

POST/manager/webhook/{id}/test

Envoie un événement de test à l'URL du webhook pour vérifier que votre serveur répond correctement.

curl -X POST "https://hou.la/api/manager/webhook/a1b2c3d4-.../test" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Réponse

200 OK

{
  "success": true,
  "statusCode": 200,
  "responseTimeMs": 142
}

Consulter les logs

GET/manager/webhook/{id}/logs

Paramètres query

ParamètreTypeDescription
pagenumberNuméro de page (défaut: 1)
limitnumberRésultats par page (défaut: 20, max: 100)
successbooleanFiltrer par succès (true) ou échec (false)
curl -X GET "https://hou.la/api/manager/webhook/a1b2c3d4-.../logs?page=1&limit=10&success=true" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Régénérer le secret

POST/manager/webhook/{id}/regenerate-secret

Génère un nouveau secret de signature. L'ancien secret sera invalidé immédiatement.

curl -X POST "https://hou.la/api/manager/webhook/a1b2c3d4-.../regenerate-secret" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Vérification de signature

Chaque requête webhook inclut un en-tête X-Houla-Signature contenant une signature HMAC-SHA256. Vérifiez cette signature pour vous assurer que la requête provient bien de hou.la.

En-têtes envoyés

En-têteDescription
X-Houla-SignatureSignature HMAC-SHA256 du body
X-Houla-EventType d'événement (ex: link.clicked)
X-Houla-DeliveryUUID unique de la livraison
X-Houla-TimestampTimestamp ISO 8601 de l'envoi

Exemple de vérification (Node.js)

const crypto = require('crypto');

function verifyWebhookSignature(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(body))
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Dans votre handler Express
app.post('/webhooks/houla', (req, res) => {
  const signature = req.headers['x-houla-signature'];
  const event = req.headers['x-houla-event'];

  if (!verifyWebhookSignature(req.body, signature, WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  switch (event) {
    case 'link.clicked':
      console.log('Clic sur', req.body.link.shortUrl);
      break;
    case 'profile.visited':
      console.log('Visite page bio', req.body.profile.slug);
      break;
  }

  res.status(200).json({ received: true });
});
Bonnes pratiques : Vérifiez toujours la signature, répondez en moins de 5 secondes (traitez en arrière-plan si nécessaire), et retournez un code 2xx pour confirmer la réception. En cas d'échec, hou.la réessaie automatiquement avec un backoff exponentiel.

12. Pixel Presets

Gérez des presets de pixels de retargeting pour pré-remplir automatiquement vos liens lors de la création.

Lister les presets

GET /api/manager/pixel-preset
Authorization: Bearer <token>

Créer un preset

POST /api/manager/pixel-preset
Authorization: Bearer <token>
Content-Type: application/json

{
  "name": "Mon site e-commerce",
  "isDefault": true,
  "fbPixelId": "123456789012345",
  "googleTagId": "G-AB1CDE2FGH"
}
ParamètreTypeRequisDescription
namestringOuiNom du preset (1-100 caractères)
isDefaultbooleanNonDéfinir comme preset par défaut pour les nouveaux liens
fbPixelIdstringNonFacebook Pixel ID (10-20 chiffres)
googleTagIdstringNonGoogle Tag ID (G-XXX, AW-XXX, DC-XXX ou UA-XXX)
tiktokPixelIdstringNonTikTok Pixel ID (commence par C, 11-31 caractères)

Modifier un preset

PATCH /api/manager/pixel-preset/:id
Authorization: Bearer <token>

Supprimer un preset

DELETE /api/manager/pixel-preset/:id
Authorization: Bearer <token>

13. Domaines personnalisés

Ajoutez vos propres domaines pour personnaliser vos liens courts. Cette fonctionnalité est réservée aux plans payants.

Plan requis : Les domaines personnalisés sont disponibles à partir du plan Premium.

Ajouter un domaine

POST/domains

Ajoute un domaine personnalisé à votre compte. Vous devrez configurer un enregistrement DNS pour vérifier la propriété du domaine.

ParamètreTypeRequisDescription
domainstringOuiLe nom de domaine à ajouter (ex : links.monsite.com)
curl -X POST "https://hou.la/api/domains" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "links.monsite.com"
  }'

Réponse

201 Created

{
  "id": "a1b2c3d4-...",
  "domain": "links.monsite.com",
  "status": "pending",
  "verificationMethod": "cname",
  "verificationToken": "houla-verify-abc123...",
  "dnsVerified": false,
  "sslConfigured": false,
  "isActive": false,
  "cnameTarget": "custom.hou.la",
  "txtRecordName": "_houla-verify.links.monsite.com",
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-01-15T10:30:00.000Z"
}

Lister les domaines

GET/domains

curl -X GET "https://hou.la/api/domains" \
  -H "Authorization: Bearer <token>"

Réponse

200 OK

[
  {
    "id": "a1b2c3d4-...",
    "domain": "links.monsite.com",
    "status": "active",
    "verificationMethod": "cname",
    "dnsVerified": true,
    "sslConfigured": false,
    "isActive": true,
    "cnameTarget": "custom.hou.la",
    "txtRecordName": "_houla-verify.links.monsite.com",
    "createdAt": "2025-01-15T10:30:00.000Z",
    "updatedAt": "2025-01-15T12:00:00.000Z"
  }
]

Récupérer un domaine

GET/domains/{id}

curl -X GET "https://hou.la/api/domains/a1b2c3d4-..." \
  -H "Authorization: Bearer <token>"

Vérifier un domaine

POST/domains/{id}/verify

Déclenche la vérification DNS du domaine. Le système vérifie que l’enregistrement CNAME ou TXT est correctement configuré.

curl -X POST "https://hou.la/api/domains/a1b2c3d4-.../verify" \
  -H "Authorization: Bearer <token>"

Réponse

200 OK — Domaine vérifié avec succès

{
  "id": "a1b2c3d4-...",
  "domain": "links.monsite.com",
  "status": "active",
  "dnsVerified": true,
  "dnsVerifiedAt": "2025-01-15T12:00:00.000Z",
  "isActive": true
}

Changer la méthode de vérification

PATCH/domains/{id}/verification-method

Change la méthode de vérification DNS du domaine (CNAME ou TXT).

ParamètreTypeRequisDescription
methodstringOuiMéthode de vérification : « cname » ou « txt »
curl -X PATCH "https://hou.la/api/domains/a1b2c3d4-.../verification-method" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "txt"
  }'

Supprimer un domaine

DELETE/domains/{id}

curl -X DELETE "https://hou.la/api/domains/a1b2c3d4-..." \
  -H "Authorization: Bearer <token>"

200 OK

{
  "success": true
}

Configuration DNS

Selon la méthode de vérification choisie, configurez l’un des enregistrements DNS suivants :

Méthode CNAME (recommandée)

links.monsite.com.  CNAME  custom.hou.la.

Méthode TXT

_houla-verify.links.monsite.com.  TXT  "houla-verify-abc123..."
Statuts possibles : pending (en attente de vérification), active (vérifié et actif), failed (échec de vérification).

14. Codes d’erreur

CodeDescription
400Requête invalide - Vérifiez les paramètres envoyés
401Non autorisé - Clé API manquante ou invalide
403Interdit - Clé API révoquée ou accès refusé
404Non trouvé - La ressource demandée n'existe pas
429Trop de requêtes - Limite de débit atteinte ou protection de sécurité activée
500Erreur serveur - Contactez le support

15. Limites de requêtes (Rate Limiting)

L'API applique des limites de requêtes selon le type d'authentification utilisé :

Type d'utilisateurLimiteTTL
Anonyme10 req/min60s
JWT authentifié100 req/min60s
Clé API1000 req/min60s
PAGES.API_DOC.RATE_LOGIN5 req/min60s
PAGES.API_DOC.RATE_GET_SKIP
PAGES.API_DOC.RATE_HEADERS_TITLEPAGES.API_DOC.RATE_HEADERS_DESC
429 Too Many Requests : Si vous dépassez ces limites, l'API retournera une erreur 429. Attendez la fin du TTL avant de réessayer.
Sécurité renforcée : Les liens protégés par mot de passe bénéficient d'une protection supplémentaire. Cette protection est indépendante du rate limiting général.

16. Bonnes pratiques

Pour une utilisation optimale de l'API, nous recommandons de :

  • Conserver votre clé API en sécurité et ne jamais la partager publiquement
  • Utiliser des variables d'environnement pour stocker vos clés
  • Éviter de créer des requêtes en boucle trop rapides
  • Gérer les erreurs et implémenter une logique de retry avec backoff

17. SDK officiel (NPM)

Le SDK houla-sdk vous permet d'intégrer rapidement l'API Hou.la dans vos projets Node.js/TypeScript.

Installation

npm install @houla/sdk

Utilisation

import { HoulaClient } from '@houla/sdk';

const client = new HoulaClient('houla_sk_xxxxxxxx');

// Créer un lien
const link = await client.createLink({
  url: 'https://example.com/ma-page',
  title: 'Mon lien'
});

console.log(link.shortUrl); // https://hou.la/xY7k9

// Créer un lien protégé par mot de passe
const protectedLink = await client.createLink({
  url: 'https://example.com/secret',
  title: 'Document confidentiel',
  password: 'secret123'
});

// Lister les liens
const result = await client.getLinks({ page: 1, limit: 10 });

// Récupérer un lien par ID
const details = await client.getLinkById('abc123');

// Supprimer un lien
await client.deleteLink('abc123');

// ─── Webhooks ───

// Créer un webhook
const webhook = await client.createWebhook({
  name: 'Mon webhook',
  url: 'https://my-app.com/webhooks/houla',
  events: ['link.clicked', 'link.created'],
  anonymizeIp: true
});
console.log(webhook.secret); // À conserver !

// Lister les webhooks
const webhooks = await client.getWebhooks();

// Tester un webhook
const test = await client.testWebhook(webhook.id);
console.log(test.success, test.responseTimeMs);

// Consulter les logs
const logs = await client.getWebhookLogs(webhook.id, 1, 20);
Documentation :Consultez le README du SDK pour la liste complète des méthodes disponibles.

18. Support

Pour toute question concernant l'API, contactez-nous à : hello@hou.la

Vous pouvez également consulter notre page de support pour plus d'informations.