# Cliqq : documentation API v1 et serveur MCP

> Cliqq est la plateforme française de liens intelligents : liens courts brandés, QR codes dynamiques, deeplinks, pages link-in-bio et analytics cookieless avec suivi des conversions. Cette page regroupe tout ce qu'un agent ou un développeur doit savoir pour utiliser l'API REST v1, le serveur MCP et les webhooks sortants.

Conventions générales :

- Base de l'API REST : `https://cliqq.fr/api/v1`. Toutes les requêtes se font en HTTPS, toutes les réponses sont en JSON UTF-8.
- Envoyez toujours l'en-tête `Accept: application/json` pour recevoir des erreurs structurées plutôt que des redirections HTML.
- Les identifiants publics sont des ULID (26 caractères), jamais des identifiants incrémentaux.
- Les montants sont exprimés en centimes (entiers). Les dates sont au format ISO 8601 (UTC).

## Authentification

L'authentification utilise des tokens personnels (Bearer), créés depuis la page Réglages, API et webhooks de l'application (`https://cliqq.fr/settings/api`). L'accès API fait partie du plan Agence.

- Chaque token est scopé à un seul workspace : toutes les ressources lues ou créées appartiennent à ce workspace, sans exception.
- Le token n'est affiché qu'une seule fois à sa création.
- Un token porte des permissions (abilities) choisies à la création :

| Permission | Description |
|---|---|
| `links:read` | Lister et consulter les liens du workspace. |
| `links:write` | Créer, modifier et supprimer des liens (et créer des QR via MCP). |
| `stats:read` | Lire les statistiques des liens et du workspace. |

Exemple d'appel authentifié :

```bash
curl "https://cliqq.fr/api/v1/links" \
  -H "Authorization: Bearer VOTRE_TOKEN" \
  -H "Accept: application/json"
```

Un appel avec un token dépourvu de la permission requise renvoie une erreur 403 explicite.

## Limites de taux

- API REST v1 et serveur MCP : 120 requêtes par minute et par token.
- Au-delà, la réponse est `429 Too Many Requests` avec un en-tête `Retry-After` indiquant le délai d'attente en secondes.
- Pour les gros volumes, privilégiez la pagination par curseur et les webhooks sortants plutôt que le polling.

## Format des erreurs

Les erreurs suivent un format JSON uniforme : un champ `message` lisible, et un objet `errors` par champ pour les erreurs de validation.

```json
{
  "message": "The destination url field is required.",
  "errors": {
    "destination_url": ["The destination url field is required."]
  }
}
```

| Code | Signification | Cas typiques |
|---|---|---|
| 401 | Unauthorized | Token absent, invalide ou révoqué. |
| 403 | Forbidden | Permission manquante sur le token, ou lien bloqué par l'anti-abus. |
| 404 | Not Found | Ressource introuvable dans le workspace du token. |
| 422 | Unprocessable | Validation échouée, slug réservé ou quota du plan atteint. |
| 429 | Too Many Requests | Limite de 120 requêtes par minute dépassée. |

## Endpoints REST v1

Un lien est identifié publiquement par son `ulid`. Les listes sont paginées par curseur, 25 éléments par page : suivez `links.next` ou passez `?cursor=`.

### GET /api/v1/links

Liste les liens du workspace du token, du plus récent au plus ancien. Permission : `links:read`.

Paramètres de requête (tous optionnels) :

| Paramètre | Type | Description |
|---|---|---|
| `status` | string | Filtre par statut : `active`, `paused`, `banned` ou `archived`. |
| `search` | string | Recherche dans le titre, le slug et l'URL de destination. |
| `cursor` | string | Curseur de pagination renvoyé par la page précédente. |

```bash
curl "https://cliqq.fr/api/v1/links?status=active&search=promo" \
  -H "Authorization: Bearer VOTRE_TOKEN" \
  -H "Accept: application/json"
```

Réponse 200 :

```json
{
  "data": [
    {
      "ulid": "01JC4X7Q9J2M3N4P5Q6R7S8T9U",
      "title": "Promo rentrée",
      "slug": "promo",
      "domain": "cliqq.fr",
      "short_url": "https://cliqq.fr/promo",
      "destination_url": "https://boutique.example.com/rentree",
      "status": "active",
      "type": "standard",
      "clicks_count": 128,
      "tags": ["campagne", "rentree"],
      "created_at": "2026-07-01T09:24:11+00:00"
    }
  ],
  "links": {
    "first": null,
    "last": null,
    "prev": null,
    "next": "https://cliqq.fr/api/v1/links?cursor=eyJpZCI6MTAxfQ"
  },
  "meta": {
    "path": "https://cliqq.fr/api/v1/links",
    "per_page": 25,
    "next_cursor": "eyJpZCI6MTAxfQ",
    "prev_cursor": null
  }
}
```

### POST /api/v1/links

Crée un lien court dans le workspace du token. Permission : `links:write`. Sans `slug`, un slug court est généré automatiquement. Sans `domain`, le lien est créé sur le domaine système par défaut. Le lien part immédiatement en scan anti-abus.

Corps de la requête :

| Champ | Type | Requis | Description |
|---|---|---|---|
| `destination_url` | string | oui | URL de destination (2048 caractères max). |
| `domain` | string | non | Hostname d'un domaine custom ACTIF de votre organisation. Par défaut : domaine système. |
| `slug` | string | non | 2 à 64 caractères parmi lettres, chiffres, tirets et underscores. Doit être libre et non réservé. |
| `title` | string | non | Titre interne du lien (255 caractères max). |
| `tags` | string[] | non | Jusqu'à 20 tags (50 caractères max chacun), créés à la volée dans le workspace. |

```bash
curl -X POST "https://cliqq.fr/api/v1/links" \
  -H "Authorization: Bearer VOTRE_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "destination_url": "https://boutique.example.com/rentree",
    "slug": "promo",
    "title": "Promo rentrée",
    "tags": ["campagne"]
  }'
```

Réponse 201 :

```json
{
  "data": {
    "ulid": "01JC4X7Q9J2M3N4P5Q6R7S8T9U",
    "title": "Promo rentrée",
    "slug": "promo",
    "domain": "cliqq.fr",
    "short_url": "https://cliqq.fr/promo",
    "destination_url": "https://boutique.example.com/rentree",
    "status": "active",
    "type": "standard",
    "clicks_count": 0,
    "tags": ["campagne"],
    "created_at": "2026-07-01T09:24:11+00:00"
  }
}
```

Si le quota de liens du plan est atteint, la réponse est 422 : les liens existants continuent de rediriger, seule la création est bloquée. Un slug réservé par Cliqq ou un domaine étranger ou inactif renvoie aussi 422.

### GET /api/v1/links/{ulid}

Consulte un lien du workspace du token. Permission : `links:read`. Réponse 200 avec le même objet `data` que la création, 404 si le lien n'appartient pas au workspace.

```bash
curl "https://cliqq.fr/api/v1/links/01JC4X7Q9J2M3N4P5Q6R7S8T9U" \
  -H "Authorization: Bearer VOTRE_TOKEN" \
  -H "Accept: application/json"
```

### PATCH /api/v1/links/{ulid}

Modifie un lien du workspace du token. Permission : `links:write`. Tous les champs sont optionnels.

| Champ | Type | Description |
|---|---|---|
| `title` | string ou null | Nouveau titre interne. |
| `destination_url` | string | Nouvelle URL de destination. Les QR imprimés suivront immédiatement. |
| `status` | string | `active`, `paused` ou `archived`. Un lien `banned` ne peut pas être réactivé via l'API (réponse 403). |

```bash
curl -X PATCH "https://cliqq.fr/api/v1/links/01JC4X7Q9J2M3N4P5Q6R7S8T9U" \
  -H "Authorization: Bearer VOTRE_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"status": "paused"}'
```

Réponse 200 avec l'objet `data` du lien mis à jour.

### DELETE /api/v1/links/{ulid}

Supprime un lien du workspace du token. Permission : `links:write`. Réponse 204 sans corps. Le lien cesse de rediriger : préférez `status: archived` si un QR imprimé pointe encore dessus.

```bash
curl -X DELETE "https://cliqq.fr/api/v1/links/01JC4X7Q9J2M3N4P5Q6R7S8T9U" \
  -H "Authorization: Bearer VOTRE_TOKEN" \
  -H "Accept: application/json"
```

### GET /api/v1/links/{ulid}/stats

Statistiques d'un lien, servies depuis des agrégats journaliers (analytics cookieless, conformité CNIL). Permission : `stats:read`.

| Paramètre | Type | Description |
|---|---|---|
| `period` | string | `7d`, `30d` (défaut) ou `90d`. |

```bash
curl "https://cliqq.fr/api/v1/links/01JC4X7Q9J2M3N4P5Q6R7S8T9U/stats?period=30d" \
  -H "Authorization: Bearer VOTRE_TOKEN" \
  -H "Accept: application/json"
```

Réponse 200 (abrégée) :

```json
{
  "data": {
    "period": "30d",
    "totals": {
      "clicks": 1240,
      "uniques": 890,
      "scans": 312,
      "conversions": 18,
      "revenue_cents": 145600
    },
    "timeseries": [
      { "date": "2026-06-22", "clicks": 42, "uniques": 31 }
    ],
    "countries": [{ "code": "FR", "count": 980 }],
    "devices": [{ "device": "mobile", "count": 720 }],
    "referers": [{ "host": "instagram.com", "count": 410 }],
    "browsers": [{ "browser": "Chrome", "count": 640 }],
    "os": [{ "os": "iOS", "count": 380 }],
    "conversions": { "count": 18, "revenue_cents": 145600 }
  }
}
```

### GET /api/v1/workspace/stats

Statistiques agrégées du workspace du token. Permission : `stats:read`. Même paramètre `period` et même structure que les statistiques d'un lien, sans le détail `conversions`, avec en plus `top_links` : les liens les plus cliqués de la période.

```bash
curl "https://cliqq.fr/api/v1/workspace/stats?period=7d" \
  -H "Authorization: Bearer VOTRE_TOKEN" \
  -H "Accept: application/json"
```

Extrait de la réponse 200 :

```json
{
  "data": {
    "period": "7d",
    "totals": {
      "clicks": 3210,
      "uniques": 2140,
      "scans": 480,
      "conversions": 41,
      "revenue_cents": 312400
    },
    "top_links": [
      {
        "ulid": "01JC4X7Q9J2M3N4P5Q6R7S8T9U",
        "title": "Promo rentrée",
        "slug": "promo",
        "short_url": "https://cliqq.fr/promo",
        "clicks": 1240
      }
    ]
  }
}
```

## Serveur MCP

Cliqq expose un serveur MCP (Model Context Protocol) pour les agents IA comme Claude Code ou Cursor. Transport : Streamable HTTP (JSON-RPC 2.0), version de protocole 2025-06-18.

- Endpoint : `POST https://cliqq.fr/mcp`
- Authentification : identique à l'API REST, token personnel dans l'en-tête `Authorization: Bearer`. Plan Agence requis, actions scopées au workspace du token, permissions du token appliquées par outil, limite de 120 requêtes par minute.

Configuration avec Claude Code :

```bash
claude mcp add --transport http cliqq https://cliqq.fr/mcp \
  --header "Authorization: Bearer VOTRE_TOKEN"
```

Configuration avec Cursor ou tout client MCP compatible HTTP (`.cursor/mcp.json`) :

```json
{
  "mcpServers": {
    "cliqq": {
      "url": "https://cliqq.fr/mcp",
      "headers": {
        "Authorization": "Bearer VOTRE_TOKEN"
      }
    }
  }
}
```

Outils disponibles :

| Outil | Permission | Paramètres | Description |
|---|---|---|---|
| `create_link` | `links:write` | `destination_url` (requis), `slug`, `title` | Crée un lien court sur le domaine par défaut, dans le workspace du token. Slug généré si absent. Quota du plan et scan anti-abus appliqués. |
| `list_links` | `links:read` | `search`, `status`, `page` | Liste les liens du workspace par pages de 25, du plus récent au plus ancien. Recherche sur titre, slug et destination, filtre par statut. |
| `get_link_stats` | `stats:read` | `link` (ULID, requis), `period` (`7d`, `30d`, `90d`) | Statistiques d'un lien : clics, uniques, scans, conversions, série journalière et répartitions. |
| `create_qr_code` | `links:write` | `link` (ULID, requis), `name` | Crée un QR dynamique pour un lien existant (un seul QR par lien). Le résultat contient l'image PNG du QR encodée en base64 (`png_base64`). Quota de QR appliqué. |
| `list_bio_pages` | `links:read` | aucun | Liste les pages link-in-bio du workspace, avec URL publique, statut de publication et nombre de blocs. |

Gestion des erreurs MCP :

- Les erreurs métier (quota atteint, slug réservé, lien introuvable, QR déjà existant) sont renvoyées comme résultats d'outil en erreur (`isError: true`) avec un message explicite en français.
- Les erreurs de protocole suivent les codes JSON-RPC standard : `-32601` pour une méthode inconnue, `-32602` pour un outil inexistant ou des paramètres de protocole invalides.

## Webhooks sortants

Configurez vos endpoints depuis la page Réglages, API et webhooks (`https://cliqq.fr/settings/api`). Plan Agence requis. Cliqq envoie un POST JSON à chaque événement souscrit.

Événements disponibles :

- `link.created` : un lien vient d'être créé dans l'organisation.
- `conversion.recorded` : une conversion vient d'être enregistrée.

Enveloppe commune :

```json
{
  "event": "link.created",
  "data": {},
  "timestamp": "2026-07-01T09:24:12+00:00"
}
```

Payload `link.created` (champ `data`) :

```json
{
  "ulid": "01JC4X7Q9J2M3N4P5Q6R7S8T9U",
  "title": "Promo rentrée",
  "slug": "promo",
  "short_url": "https://cliqq.fr/promo",
  "destination_url": "https://boutique.example.com/rentree",
  "workspace": "01JC4WZ0A1B2C3D4E5F6G7H8J9"
}
```

Payload `conversion.recorded` (champ `data`) :

```json
{
  "id": "01JC4Y2B3C4D5E6F7G8H9J0K1L",
  "type": "purchase",
  "value_cents": 4900,
  "currency": "EUR",
  "link_ulid": "01JC4X7Q9J2M3N4P5Q6R7S8T9U",
  "occurred_at": "2026-07-01T10:02:45+00:00"
}
```

Signature et livraison :

- Chaque livraison porte l'en-tête `X-Cliqq-Signature` : HMAC-SHA256 hexadécimal du corps brut de la requête, calculé avec le secret du webhook (affiché une seule fois à sa création). Vérifiez la signature en comparaison à temps constant avant tout traitement.
- Répondez avec un code 2xx en moins de 10 secondes.
- En cas d'échec, la livraison est retentée 2 fois : après 60 secondes, puis après 300 secondes (3 tentatives au total).

Vérification de signature en PHP :

```php
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_CLIQQ_SIGNATURE'] ?? '';

$expected = hash_hmac('sha256', $rawBody, $secret);

if (! hash_equals($expected, $signature)) {
    http_response_code(401);
    exit;
}
```

## Ressources

- Documentation interactive (membres connectés) : `https://cliqq.fr/app/docs`
- Index LLM : `https://cliqq.fr/llms.txt`
- Tarifs et plans : `https://cliqq.fr/tarifs`
