API REST · HTTPS · JSON

Référence API

Trois couches d'API : V1 (tokens custom — extension Chrome & intégrations existantes), V2 Platform (tokens scopés, idempotency, webhooks, Zapier) et Agency Partner API (gestion clients & commissions). Change Control intégré — toute modification de destination sur un asset physique passe par un workflow d'approbation.

Authentification

Bearer token généré depuis votre profil.

Voir →

Liens courts

Créez, modifiez, supprimez et obtenez les stats.

Voir →

QR Codes

Générez et gérez vos QR codes par programme.

Voir →

Modules

Produits numériques, réservations et cours.

Voir →

Change Control

Workflow d'approbation pour les assets physiques.

Voir →

V2 Platform API

Tokens scopés, idempotency, webhooks, Zapier.

Voir →

Webhooks sortants

Abonnements, livraisons et événements V2.

Voir →

Agency Partner API

Gestion de clients et commissions pour agences.

Voir →

URL de base

https://frqr.app/api/v1

Toutes les requêtes doivent utiliser HTTPS. Limite : 60 requêtes / minute par token.

Authentification

Chaque requête V1 doit inclure votre clé API dans l'en-tête Authorization. Utilisée par l'extension Chrome et les intégrations existantes — préférez la V2 pour les nouvelles intégrations.

Format du header

curl https://frqr.app/api/v1/short-links \
  -H "Authorization: Bearer VOTRE_CLÉ_API"
const res = await fetch('https://frqr.app/api/v1/short-links', {
  headers: {
    'Authorization': 'Bearer VOTRE_CLÉ_API',
    'Content-Type': 'application/json',
  }
});
const data = await res.json();
$response = Http::withToken('VOTRE_CLÉ_API')
    ->get('https://frqr.app/api/v1/short-links');

$data = $response->json();

Créez votre clé API depuis votre profil : frqr.app/profile. La clé n'est affichée qu'une seule fois — conservez-la en lieu sûr.

Profil

Récupérez les informations du compte associé à votre clé API.

Retourne les informations du compte authentifié : identité, plan souscrit et quota de liens courts.

Réponse

{
  "id": 123,
  "name": "Marie Dupont",
  "email": "marie@example.com",
  "plan": "pro",
  "short_link_limit": 5000,
  "short_links_count": 127
}

QR Codes

Créez, listez, modifiez et supprimez vos QR codes. Accédez aux statistiques géolocalisées.

Retourne vos QR codes (50 par page, du plus récent au plus ancien).

Paramètres

per_page integer optionnel Résultats par page — max 100, défaut 50
page integer optionnel Numéro de page

Réponse

{"data":[{"id":1,"name":"Menu restaurant","url":"https://example.com/menu","type":"url","status":"active","fallback_url":null,"expires_at":null,"created_at":"2024-01-15T10:30:00Z"}],"meta":{"current_page":1,"total":8}}

Génère un nouveau QR code à partir d'une URL.

Paramètres

url string requis URL encodée dans le QR code
name string requis Nom du QR code
foreground_color string optionnel Couleur de premier plan (#hex)
background_color string optionnel Couleur de fond (#hex)
fallback_url string optionnel URL de secours si le QR est expiré
expires_at datetime optionnel Date d'expiration ISO 8601 (nullable)

Réponse

{"id":42,"name":"Menu restaurant","url":"https://example.com/menu","type":"url","status":"active","fallback_url":null,"expires_at":null,"created_at":"2024-01-15T10:30:00Z"}

Met à jour les propriétés d'un QR code dynamique. Même logique change control que les liens courts : 202 si workspace collaboratif, 423 si asset gelé.

⚡ Change Control : même comportement que PATCH /short-links/{slug} — 202 si workspace collaboratif, 423 si asset gelé.

Paramètres

url string optionnel Nouvelle URL de destination
name string optionnel Nouveau nom
fg_color string optionnel Couleur de premier plan (#hex)
bg_color string optionnel Couleur de fond (#hex)
fallback_url string optionnel URL de secours si expiré (nullable)
expires_at datetime optionnel Date d'expiration ISO 8601 (nullable)

Réponse

// 200 OK — mise à jour directe
{"id":42,"name":"Menu restaurant","url":"https://nouveau.com/menu","status":"active","updated_at":"2024-01-16T08:00:00Z"}

// 202 Accepted — soumis pour approbation
{"data":{"id":"cr_xyz789","status":"pending_approval","change_request_id":"cr_xyz789","field":"url","requested_value":"https://nouveau.com/menu","message":"Destination change submitted for approval."}}

Supprime définitivement un QR code.

Réponse

{"message":"deleted"}

Retourne les scans par jour, par pays, par appareil et par ville sur la période demandée.

Paramètres

start_date date optionnel Début de période YYYY-MM-DD (défaut : -30 jours)
end_date date optionnel Fin de période YYYY-MM-DD (défaut : aujourd'hui)

Réponse

{"id":42,"total_scans":389,"daily":[{"date":"2024-01-15","scans":22}],"by_country":[{"country":"FR","scans":210}],"by_device":[{"device":"mobile","scans":301}],"by_city":[{"city":"Paris","scans":88},{"city":"Marseille","scans":34}]}

Modules

Accédez à vos produits numériques, réservations et cours.

Plan requis — disponibilité selon votre souscription

Produits numériques

Retourne vos produits numériques publiés (10 par page par défaut).

Paramètres

per_page integer optionnel Nombre de résultats par page (défaut : 10)

Réponse

{"data":[{"id":1,"name":"Guide SEO 2024","slug":"guide-seo-2024","price":29.00,"currency":"EUR","category":"ebook","description":"...","cover_image_url":"https://...","download_type":"file","total_sales":48,"total_revenue":1392.00}],"pagination":{"total":3,"per_page":10,"current_page":1,"last_page":1}}

Retourne les détails complets d'un produit numérique.

Réponse

{"id":1,"name":"Guide SEO 2024","slug":"guide-seo-2024","price":29.00,"currency":"EUR","category":"ebook","description":"Guide complet du référencement naturel.","cover_image_url":"https://...","download_type":"file","total_sales":48,"total_revenue":1392.00}

Réservations

Retourne vos créneaux de réservation publiés (10 par page par défaut).

Paramètres

per_page integer optionnel Nombre de résultats par page (défaut : 10)

Réponse

{"data":[{"id":1,"name":"Consultation 30 min","slug":"consultation-30min","description":"...","duration":30,"price":50.00,"currency":"EUR","timezone":"Europe/Paris","location_type":"video","location":"https://meet.example.com","capacity":1,"max_appointments_per_day":8,"total_bookings":24,"total_revenue":1200.00}],"pagination":{"total":2,"per_page":10,"current_page":1,"last_page":1}}

Retourne les détails complets d'un créneau de réservation.

Réponse

{"id":1,"name":"Consultation 30 min","slug":"consultation-30min","description":"Échange découverte de 30 minutes.","duration":30,"price":50.00,"currency":"EUR","timezone":"Europe/Paris","location_type":"video","location":"https://meet.example.com","capacity":1,"max_appointments_per_day":8,"total_bookings":24,"total_revenue":1200.00}

Cours

Retourne vos cours publiés (10 par page par défaut).

Paramètres

per_page integer optionnel Nombre de résultats par page (défaut : 10)

Réponse

{"data":[{"id":1,"title":"Maîtrisez Laravel 12","slug":"maitrisez-laravel-12","description":"...","price":99.00,"currency":"EUR","category":"développement","language":"fr","level":"intermédiaire","thumbnail_url":"https://...","total_lessons":42,"total_enrollments":318,"average_completion":0.74}],"pagination":{"total":1,"per_page":10,"current_page":1,"last_page":1}}

Retourne les détails complets d'un cours.

Réponse

{"id":1,"title":"Maîtrisez Laravel 12","slug":"maitrisez-laravel-12","description":"Formation complète pour construire des apps SaaS.","price":99.00,"currency":"EUR","category":"développement","language":"fr","level":"intermédiaire","thumbnail_url":"https://...","completion_requirement":"all_lessons","certificate_enabled":true,"total_lessons":42,"total_enrollments":318,"average_completion":0.74}

Change Control

Workflow d'approbation pour les modifications de destination sur vos assets physiques (QR codes imprimés, liens sur supports offline).

Activé automatiquement quand votre espace de travail a des collaborateurs.

Toute modification de destination_url via PATCH retourne 202 Accepted (au lieu de 200) et crée une demande de changement en attente d'approbation par le propriétaire de l'espace.

Les assets gelés (is_frozen: true) retournent 423 Locked quel que soit le rôle.

Retourne les demandes de changement de votre espace de travail (les vôtres si collaborateur, toutes si propriétaire).

Paramètres

status string optionnel Filtre : pending, approved ou rejected
per_page integer optionnel Résultats par page — max 100, défaut 20

Réponse

{"data":[{"id":"cr_xyz789","status":"pending","source":"api","asset_type":"ShortLink","asset_id":"abc123","field":"destination_url","old_value":"https://ancien.com","new_value":"https://nouveau.com","requested_by":{"id":45,"name":"Thomas Martin"},"decided_by":null,"decided_at":null,"created_at":"2024-01-16T08:00:00Z"}],"meta":{"current_page":1,"total":3}}

Retourne le détail complet d'une demande de changement.

Réponse

{"id":"cr_xyz789","status":"pending","source":"api","asset_type":"ShortLink","asset_id":"abc123","field":"destination_url","old_value":"https://ancien.com","new_value":"https://nouveau.com","requested_by":{"id":45,"name":"Thomas Martin"},"decided_by":null,"decided_at":null,"created_at":"2024-01-16T08:00:00Z"}

Approuve la demande et applique immédiatement la modification sur l'asset. Réservé au propriétaire de l'espace.

Réponse

{"id":"cr_xyz789","status":"approved","decided_by":{"id":1,"name":"Marie Dupont"},"decided_at":"2024-01-16T09:30:00Z","applied_at":"2024-01-16T09:30:00Z"}

Rejette la demande sans appliquer la modification. Réservé au propriétaire de l'espace.

Paramètres

reason string optionnel Motif du rejet (transmis au demandeur via webhook)

Réponse

{"id":"cr_xyz789","status":"rejected","decided_by":{"id":1,"name":"Marie Dupont"},"decided_at":"2024-01-16T09:35:00Z","reason":"Destination non validée par le client."}

Trois événements sont émis sur l'URL webhook configurée dans votre espace de travail. Configurez votre webhook dans Paramètres → Webhooks.

change_request.created

Émis quand une demande est soumise. Payload : asset_type, asset_id, field, old_value, new_value, requested_by.

change_request.approved

Émis quand le propriétaire approuve. Payload : change_request_id, decided_by, applied_at.

change_request.rejected

Émis quand le propriétaire rejette. Payload : change_request_id, decided_by, reason.

Exemple — Cycle complet en PHP

// 1. Modifier la destination d'un lien
$res = Http::withToken($token)
    ->patch("https://frqr.app/api/v1/short-links/{$slug}", [
        'destination_url' => 'https://nouvelle-destination.com',
    ]);

if ($res->status() === 202) {
    // Change control actif — stocker le change_request_id
    $crId = $res->json('data.change_request_id'); // "cr_xyz789"
    // → Attendre le webhook change_request.approved
}

// 2. Handler webhook (votre endpoint public)
$payload = json_decode(file_get_contents('php://input'), true);

if ($payload['event'] === 'change_request.approved') {
    $crId = $payload['data']['change_request_id'];
    // → Reprendre l'automation, la modification est appliquée
    applyPendingAction($crId);
}

if ($payload['event'] === 'change_request.rejected') {
    $reason = $payload['data']['reason'] ?? 'Pas de motif';
    // → Notifier l'utilisateur ou logger le rejet
    notifyRejection($crId, $reason);
}

Apps mobiles — Universal Links & Deferred Deep Linking

Enregistrez vos apps iOS/Android dans le panel admin pour que frqr.app serve automatiquement les fichiers .well-known/apple-app-site-association et .well-known/assetlinks.json adaptés à votre domaine. Le deferred deep linking permet à vos apps de récupérer la destination après une première installation.

GET /.well-known/apple-app-site-association

Fichier AASA servi par host. Public, sans authentification. Apple le crawle à l'installation de l'app.

{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "TEAMID.com.exemple.monapp",
        "paths": ["/*"]
      }
    ]
  }
}
GET /.well-known/assetlinks.json

Fichier Asset Links Android. Google le crawle à l'installation. Nécessite un ou plusieurs fingerprints SHA-256.

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.exemple.monapp",
      "sha256_cert_fingerprints": ["AA:BB:CC:..."]
    }
  }
]

Deferred Deep Linking

Le deferred deep linking est best-effort — il repose sur le fingerprinting du navigateur (UA + langue + écran + fuseau). Le taux de correspondance réel est de 70–85 % selon les navigateurs.

RGPD : aucun identifiant persistant n'est créé. Seul un hash SHA-256 non-réversible est stocké. Les enregistrements expirent automatiquement après 24h et sont purgés quotidiennement.

POST /api/v1/deferred/track · sans authentification · throttle 60/min/IP

Appelé par le JS du navigateur au moment du clic sur un short link mobile. Enregistre le fingerprint → URL cible avec TTL 24h.

Requête

// JS côté navigateur — calculer le fingerprint
async function fingerprintHash() {
  const raw = [
    navigator.userAgent,
    navigator.language,
    screen.width + 'x' + screen.height,
    Intl.DateTimeFormat().resolvedOptions().timeZone,
  ].join('|');
  const buf = await crypto.subtle.digest(
    'SHA-256',
    new TextEncoder().encode(raw)
  );
  return Array.from(new Uint8Array(buf))
    .map(b => b.toString(16).padStart(2, '0'))
    .join('');
}

const fp = await fingerprintHash();
await fetch('/api/v1/deferred/track', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    fingerprint_hash: fp,
    target_url:       'https://app.exemple.com/produit/42',
    accept_language:  navigator.language,
    screen_info:      screen.width + 'x' + screen.height,
  }),
});

Réponse 201

{ "ok": true }
POST /api/v1/deferred/match · sans authentification · throttle 10/min/IP

Appelé par l'app native au premier lancement après installation. Retourne l'URL cible si un fingerprint correspond dans la fenêtre 24h.

Swift (URLSession)

import CryptoKit
import Foundation

func fingerprintHash() -> String {
    let raw = [
        UIDevice.current.systemName + " " +
            UIDevice.current.systemVersion,
        Locale.current.language.languageCode?.identifier ?? "",
        "\(UIScreen.main.bounds.width)x\(UIScreen.main.bounds.height)",
        TimeZone.current.identifier,
    ].joined(separator: "|")
    let data   = Data(raw.utf8)
    let digest = SHA256.hash(data: data)
    return digest.map { String(format: "%02x", $0) }.joined()
}

func matchDeferredDeeplink() async throws -> URL? {
    let fp   = fingerprintHash()
    let url  = URL(string: "https://frqr.app/api/v1/deferred/match")!
    var req  = URLRequest(url: url)
    req.httpMethod = "POST"
    req.setValue("application/json", forHTTPHeaderField: "Content-Type")
    req.httpBody   = try JSONEncoder().encode(["fingerprint_hash": fp])

    let (data, _) = try await URLSession.shared.data(for: req)
    let decoded   = try JSONDecoder().decode(
        [String: String?].self, from: data
    )
    if let raw = decoded["deeplink"] as? String {
        return URL(string: raw)
    }
    return nil
}

// Dans AppDelegate / SceneDelegate — premier lancement uniquement
if !UserDefaults.standard.bool(forKey: "deferred_deeplink_checked") {
    UserDefaults.standard.set(true, forKey: "deferred_deeplink_checked")
    if let deeplink = try? await matchDeferredDeeplink() {
        // Naviguer vers deeplink
    }
}

Kotlin (OkHttp)

import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
import java.security.MessageDigest

fun fingerprintHash(context: android.content.Context): String {
    val display = context.resources.displayMetrics
    val raw = listOf(
        android.os.Build.VERSION.RELEASE,
        java.util.Locale.getDefault().language,
        "${display.widthPixels}x${display.heightPixels}",
        java.util.TimeZone.getDefault().id,
    ).joinToString("|")
    return MessageDigest.getInstance("SHA-256")
        .digest(raw.toByteArray())
        .joinToString("") { "%02x".format(it) }
}

fun matchDeferredDeeplink(context: android.content.Context): String? {
    val fp     = fingerprintHash(context)
    val client = OkHttpClient()
    val body   = JSONObject(mapOf("fingerprint_hash" to fp))
        .toString()
        .toRequestBody("application/json".toMediaType())
    val req    = Request.Builder()
        .url("https://frqr.app/api/v1/deferred/match")
        .post(body)
        .build()
    client.newCall(req).execute().use { resp ->
        val json = JSONObject(resp.body!!.string())
        return json.optString("deeplink").takeIf { it.isNotEmpty() }
    }
}

// Dans Application.onCreate ou SplashActivity — premier lancement uniquement
val prefs = getSharedPreferences("frqr", MODE_PRIVATE)
if (!prefs.getBoolean("deferred_deeplink_checked", false)) {
    prefs.edit().putBoolean("deferred_deeplink_checked", true).apply()
    val deeplink = matchDeferredDeeplink(this)
    // Naviguer vers deeplink si non null
}

Réponse — match trouvé

{ "deeplink": "https://app.exemple.com/produit/42" }

Réponse — pas de match

{ "deeplink": null }

V2 Platform API

L'API V2 utilise des tokens scopés (non-Sanctum), l'idempotency sur toutes les mutations, et un throttling par tier. Base URL : https://frqr.app/api/v2.

Authentification V2 — Tokens scopés

Les tokens V2 sont créés depuis votre profil (ou via POST /api/v2/tokens). Chaque token a une liste de scopes qui restreignent les opérations autorisées.

assets:read assets:write assets:delete workspace:read workspace:write webhooks:manage tokens:read tokens:write
Authorization: Bearer v2_TOKEN_ICI

Limites de débit par tier

Plan V1 (req/min) V2 (req/min)
lite / free 60 120
pro 60 300
scale 60 600
enterprise 60 1 200

Idempotency — header Idempotency-Key

Sur toutes les méthodes mutantes (POST, PATCH, DELETE) de la V2, envoyez un UUID unique dans Idempotency-Key. Si la même clé est réutilisée dans les 24h, la réponse originale est retournée sans re-exécution — protège contre les doubles soumissions.

POST /api/v2/short-links
Authorization: Bearer v2_…
Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Content-Type: application/json

QR Codes (V2)

Identique à V1 mais avec GET /{id} en plus, scopes requis, et base URL /api/v2.

Méthode Endpoint Scope requis
GET /api/v2/qr-codes assets:read
POST /api/v2/qr-codes assets:write
GET /api/v2/qr-codes/{id} assets:read
PATCH /api/v2/qr-codes/{id} assets:write
DELETE /api/v2/qr-codes/{id} assets:delete
GET /api/v2/qr-codes/{id}/stats assets:read

Liens courts (V2)

Adressage par id (entier) et non par slug. Route GET /{id} disponible.

Méthode Endpoint Scope requis
GET /api/v2/short-links assets:read
POST /api/v2/short-links assets:write
GET /api/v2/short-links/{id} assets:read
PATCH /api/v2/short-links/{id} assets:write
DELETE /api/v2/short-links/{id} assets:delete
GET /api/v2/short-links/{id}/stats assets:read

Workspace (V2)

Méthode Endpoint Description Scope
GET /api/v2/workspace Profil, plan, limites workspace:read
GET /api/v2/workspace/usage Quotas consommés workspace:read
GET /api/v2/workspace/invoices Historique de facturation workspace:read
GET /api/v2/workspace/members Liste des membres workspace:read
POST /api/v2/workspace/members/invite Inviter un membre workspace:write
PATCH /api/v2/workspace/members/{id} Modifier le rôle workspace:write
DELETE /api/v2/workspace/members/{id} Retirer un membre workspace:write

Webhooks sortants (V2)

Gérez vos abonnements webhook par API. Scope requis : webhooks:manage. Base URL : /api/v2/webhooks.

Retourne tous vos abonnements webhook actifs.

Réponse

{"data":[{"id":1,"url":"https://example.com/webhooks/frqr","events":["short_link.created","qr_code.created","change_request.approved"],"is_active":true,"created_at":"2024-05-01T10:00:00Z"}]}

Crée un nouvel abonnement webhook.

Le secret est affiché une seule fois — conservez-le pour vérifier la signature X-Frqr-Signature des livraisons.

Paramètres

url string requis URL HTTPS de destination
events array requis Liste des événements (ex: short_link.created, qr_code.created, change_request.approved, change_request.rejected)
secret string optionnel Secret HMAC-SHA256 pour vérifier les livraisons

Réponse

{"id":2,"url":"https://example.com/webhooks/frqr","events":["short_link.created"],"is_active":true,"created_at":"2024-05-01T12:00:00Z"}

Met à jour l'URL, les événements ou le statut actif.

Paramètres

url string optionnel Nouvelle URL
events array optionnel Nouvelle liste d'événements
is_active boolean optionnel Activer / désactiver

Réponse

{"id":2,"url":"https://example.com/webhooks/frqr-v2","events":["short_link.created","qr_code.created"],"is_active":true}

Supprime définitivement l'abonnement.

Réponse

{"message":"deleted"}

Retourne les 50 dernières tentatives de livraison (statut, durée, code HTTP, erreur éventuelle).

Réponse

{"data":[{"id":123,"event":"short_link.created","status":"delivered","http_status":200,"duration_ms":145,"attempted_at":"2024-05-01T12:05:00Z"},{"id":122,"event":"short_link.created","status":"failed","http_status":500,"duration_ms":3021,"attempted_at":"2024-05-01T11:50:00Z","error":"Connection timeout"}]}

Événements disponibles

short_link.created Nouveau lien court créé
short_link.updated Lien court modifié (destination, statut)
short_link.deleted Lien court supprimé
qr_code.created Nouveau QR code créé
qr_code.updated QR code modifié
qr_code.deleted QR code supprimé
change_request.created Demande de changement soumise
change_request.approved Demande approuvée — modification appliquée
change_request.rejected Demande rejetée
booking.appointment_confirmed Nouveau RDV confirmé

Gestion des tokens (V2)

Créez et révoquez des tokens depuis l'API. Scope requis : tokens:read / tokens:write.

Retourne vos tokens actifs avec leurs scopes et date d'expiration. La valeur brute n'est jamais renvoyée après création.

Réponse

{"data":[{"id":1,"name":"Zapier integration","scopes":["assets:read","assets:write"],"last_used_at":"2024-05-10T08:22:00Z","expires_at":null,"created_at":"2024-04-01T10:00:00Z"},{"id":2,"name":"CI/CD pipeline","scopes":["assets:read"],"last_used_at":null,"expires_at":"2025-01-01T00:00:00Z","created_at":"2024-04-15T09:00:00Z"}]}

Crée un nouveau token scopé. La valeur brute est retournée une seule fois dans la réponse de création — conservez-la immédiatement.

⚠️ La valeur du token n'est affichée qu'une seule fois. Copiez-la avant de fermer cette réponse.

Paramètres

name string requis Nom descriptif du token
scopes array requis Liste des scopes (assets:read, assets:write, assets:delete, workspace:read, workspace:write, webhooks:manage, tokens:read, tokens:write)
expires_at datetime optionnel Date d'expiration ISO 8601 (null = pas d'expiration)

Réponse

{"id":3,"name":"Mon nouveau token","scopes":["assets:read","assets:write"],"token":"v2_xxxxxxxxxxxxxxxxxxxx","expires_at":null,"created_at":"2024-05-15T12:00:00Z"}

Révoque immédiatement un token. Toutes les requêtes en cours avec ce token retournent 401 dès la révocation.

Réponse

{"message":"Token revoked"}

Intégration Zapier (V2)

frqr.app expose des triggers et actions compatibles Zapier REST hooks. Utilisez votre token V2 dans l'authentification Zapier.

Triggers (polling)

GET
/api/v2/zapier/triggers/qr-code-created

Dernier QR code créé — Zapier échantillonne pour la configuration du Zap. Scope : assets:read

GET
/api/v2/zapier/triggers/short-link-created

Dernier lien court créé. Scope : assets:read

Actions

POST
/api/v2/zapier/actions/create-qr-code

Crée un QR code depuis un Zap. Scope : assets:write

POST
/api/v2/zapier/actions/create-short-link

Crée un lien court depuis un Zap. Scope : assets:write

REST Hooks lifecycle

POST
/api/v2/zapier/subscribe

Zapier appelle cet endpoint pour s'abonner aux événements. Scope : webhooks:manage

DELETE
/api/v2/zapier/unsubscribe/{subscription}

Zapier appelle cet endpoint quand un Zap est désactivé. Scope : webhooks:manage

Agency Partner API

Réservée aux comptes agence. Gestion des clients et consultation des commissions. Base URL : https://frqr.app/api/v1/agency.

Authentification Agency — token SHA-256

Le token agency est distinct des tokens V1/V2. Il est généré depuis le panel d'agence et envoyé dans le header Authorization.

Authorization: Bearer AGENCY_TOKEN_SHA256
# Throttle : 120 req/min (GET), 60 req/min (POST/PATCH/DELETE)

Retourne la liste de tous vos clients agence (paginé).

Paramètres

per_page integer optionnel Résultats par page (défaut 20)

Réponse

{"data":[{"id":1,"name":"Client SA","email":"contact@client.com","plan":"pro","short_links_count":42,"qr_codes_count":8,"created_at":"2024-02-01T09:00:00Z"}],"meta":{"total":12,"current_page":1}}

Crée un nouveau compte client sous votre agence.

Paramètres

name string requis Nom du client
email email requis Email — doit être unique
plan string optionnel Slug du plan (lite, pro, scale…)
password string optionnel Mot de passe initial (généré si absent)

Réponse

{"id":13,"name":"Nouveau Client","email":"nouveau@client.com","plan":"lite","created_at":"2024-05-15T14:00:00Z"}

Retourne le profil complet d'un client agence.

Réponse

{"id":1,"name":"Client SA","email":"contact@client.com","plan":"pro","short_links_count":42,"qr_codes_count":8,"signature_limit":100,"short_link_limit":5000,"created_at":"2024-02-01T09:00:00Z"}

Met à jour le plan ou les limites d'un client.

Paramètres

plan string optionnel Nouveau plan slug
short_link_limit integer optionnel Limite personnalisée de liens courts
qr_code_limit integer optionnel Limite personnalisée de QR codes

Réponse

{"id":1,"name":"Client SA","plan":"scale","short_link_limit":10000}

Supprime définitivement le compte client (soft-delete, purge automatique après 30 jours RGPD).

Réponse

{"message":"deleted"}

Retourne le récapitulatif des commissions générées par vos clients : total, en attente et versé.

Réponse

{"total_earned":2840.00,"pending":320.00,"paid":2520.00,"clients_count":12,"active_clients":9,"currency":"EUR","last_updated_at":"2024-05-15T00:00:00Z"}

Webhooks entrants — Intégrations tierces

frqr.app reçoit des webhooks de services externes pour créer automatiquement des actifs (QR codes, liens courts) à partir d'événements e-commerce, formulaires et agenda.

Endpoints de réception

POST Shopify
/api/webhooks/shopify/{integrationId}

Nouveaux produits, commandes confirmées → QR code / lien court automatique. Signature HMAC-SHA256 X-Shopify-Hmac-SHA256.

POST Cal.com
/api/webhooks/calcom/{integrationId}

Nouveau RDV confirmé → lien court vers la page de visio. Signature : Authorization Bearer partagé.

POST Tally.so
/api/webhooks/tally/{integrationId}

Soumission de formulaire → enregistrement de lead ou création de lien. Signature : X-Tally-Signature.

POST Brevo
/api/webhooks/brevo/{integrationId}

Événements email (delivered, opened, clicked) → mise à jour du statut dans les analytics.

POST Webflow
/api/webhooks/webflow/{integrationId}

Publication de page → création de QR code ou lien court associé.

Chaque intégration est configurée dans le panel (Intégrations → Connecter). L'integrationId dans l'URL est généré lors de la connexion.

Codes de réponse

L'API retourne des codes HTTP standard avec un corps JSON.

200

OK

Requête traitée avec succès. La ressource est retournée dans le corps.

202

Accepted

Modification soumise pour approbation (change control actif). Corps : { data: { change_request_id, status: 'pending_approval', ... } }

401

Unauthorized

Token manquant ou invalide.

403

Forbidden

Vous n'êtes pas propriétaire de la ressource, ou cette fonctionnalité n'est pas disponible sur votre plan.

404

Not Found

Ressource introuvable.

422

Unprocessable Entity

Erreur de validation — voir le champ errors.

409

Conflict

Clé d'idempotency déjà utilisée avec un payload différent (V2 seulement). Retourne la réponse originale si le payload est identique.

423

Locked

Asset gelé. Contactez le propriétaire de l'espace pour le dégeler avant toute modification.

429

Too Many Requests

Limite de taux atteinte. V1 : 60 req/min. V2 : 120–1 200 req/min selon votre plan. Agency : 60–120 req/min.

Changelog

Historique des modifications de l'API.

Mai 2026 V2 Platform API Agency API
  • nouveau

    API V2 Platform — tokens scopés + tier throttling

    Nouvelle couche d'API sur /api/v2. Tokens scopés (8 scopes disponibles), throttling par plan (120–1 200 req/min), idempotency key sur toutes les mutations. Couvre QR codes, liens courts, workspace, webhooks et tokens.

  • nouveau

    Webhooks sortants V2 — CRUD + historique de livraisons

    Créez, modifiez et supprimez vos abonnements webhook par API. Historique des 50 dernières livraisons avec durée, code HTTP et erreur éventuelle. 10 événements disponibles dont booking.appointment_confirmed.

  • nouveau

    Gestion de tokens par API — GET /tokens, POST /tokens, DELETE /tokens/{id}

    Rotation et révocation de tokens depuis votre automation. Création avec scopes granulaires et expiration optionnelle.

  • nouveau

    Intégration Zapier — REST hooks + triggers + actions

    6 endpoints Zapier : 2 triggers polling (QR code créé, lien créé), 2 actions (créer QR code, créer lien), subscribe et unsubscribe REST hooks. Authentification via token V2.

  • nouveau

    Agency Partner API — gestion clients & commissions

    Nouvelle API agency sur /api/v1/agency. CRUD complet sur les clients agence, consultation du résumé des commissions. Authentification par token SHA-256 distinct.

  • nouveau

    Webhooks entrants — Shopify, Cal.com, Tally, Brevo, Webflow

    5 intégrations de réception configurables depuis le panel. Chaque intégration expose un endpoint unique signé avec HMAC.

  • amélioration

    Endpoint V2 GET /{id} sur QR codes et liens courts

    V2 expose désormais GET /qr-codes/{id} et GET /short-links/{id} pour récupérer un asset individuel sans lister. Adressage par ID entier (non slug).

Avril 2026 Change Control V1
  • nouveau

    Change Control — workflow d'approbation

    PATCH /short-links/{slug} et PATCH /qr-codes/{id} retournent 202 Accepted + change_request_id quand l'espace a des collaborateurs et que destination_url est modifiée.

  • nouveau

    Endpoints /change-requests

    Quatre endpoints : GET (liste), GET (détail), POST /approve, POST /reject. Permet de gérer le workflow d'approbation depuis l'API.

  • nouveau

    Webhooks change_request.*

    Trois événements : change_request.created, change_request.approved, change_request.rejected. Configurables dans Paramètres → Webhooks.

  • nouveau

    HTTP 423 Locked pour assets gelés

    Toute tentative de modification d'un asset gelé retourne 423 avec { "error": "asset_frozen", "message": "..." }.

  • nouveau

    PATCH /qr-codes/{id}

    Les QR codes dynamiques sont maintenant modifiables par API : url, name, fg_color, bg_color, fallback_url, expires_at.

  • amélioration

    Statistiques enrichies avec géolocalisation ville

    GET /short-links/{slug}/stats et GET /qr-codes/{id}/stats retournent désormais by_city, by_country, by_device, by_browser. Paramètres start_date / end_date disponibles.

  • amélioration

    Champs fallback_url, expires_at et status dans toutes les réponses

    GET, POST et PATCH sur /short-links et /qr-codes retournent maintenant fallback_url, expires_at et status (active / expired / pending_approval).

  • amélioration

    Paramètre per_page sur tous les endpoints de liste

    Toutes les listes acceptent désormais per_page jusqu'à 100 résultats (défaut 50). Réponse unifiée avec bloc meta pour la pagination.

  • sécurité

    RGPD — Anonymisation automatique des adresses IP

    Les adresses IP sont masquées avant tout stockage analytique (2 derniers octets IPv4 / 64 bits IPv6). Les données géographiques dérivées (pays, ville) sont conservées. Applicable à tous les nouveaux scans, clics et soumissions.

Prêt à commencer ?

Créez votre clé API depuis votre profil et faites votre première requête en quelques minutes.

Créer ma clé API →