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
}
Liens courts
Créez, listez, modifiez et supprimez vos liens courts. Accédez aux statistiques et leads.
Retourne la liste paginée de vos liens courts. Paramètre per_page jusqu'à 100 (défaut : 50).
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":[{"slug":"abc123","destination_url":"https://example.com","short_url":"https://frqr.app/abc123","title":"Mon lien","type":"url","status":"active","is_active":true,"fallback_url":null,"expires_at":null,"clicks":142,"created_at":"2024-01-15T10:30:00Z"}],"meta":{"current_page":1,"last_page":3,"total":127}}
Crée un nouveau lien court.
Paramètres
destination_url
string
requis
URL de destination
title
string
optionnel
Nom du lien
slug
string
optionnel
Slug personnalisé (généré si absent)
fallback_url
string
optionnel
URL de secours si le lien est expiré
expires_at
datetime
optionnel
Date d'expiration ISO 8601 (nullable)
Réponse
{"slug":"abc123","destination_url":"https://example.com","short_url":"https://frqr.app/abc123","title":"Mon lien","type":"url","status":"active","is_active":true,"fallback_url":null,"expires_at":null,"clicks":0,"created_at":"2024-01-15T10:30:00Z"}
Met à jour les propriétés d'un lien court. Si votre espace a des collaborateurs et que vous modifiez destination_url, la mise à jour est soumise pour approbation (202 Accepted). Un asset gelé retourne 423 Locked.
Paramètres
destination_url
string
optionnel
Nouvelle URL de destination
title
string
optionnel
Nouveau nom
is_active
boolean
optionnel
Activer / désactiver le lien
fallback_url
string
optionnel
URL de secours si le lien est expiré (nullable)
expires_at
datetime
optionnel
Date d'expiration ISO 8601 (nullable)
Réponse
// 200 OK — mise à jour directe (owner ou change control désactivé)
{"slug":"abc123","destination_url":"https://nouveau.com","status":"active","updated_at":"2024-01-16T08:00:00Z"}
// 202 Accepted — soumis pour approbation (workspace avec collaborateurs)
{"data":{"id":"cr_xyz789","status":"pending_approval","change_request_id":"cr_xyz789","field":"destination_url","requested_value":"https://nouveau.com","message":"Destination change submitted for approval."}}
// 423 Locked — asset gelé
{"error":"asset_frozen","message":"This asset is frozen. Contact the workspace owner to unfreeze before making changes."}
Supprime définitivement un lien court et ses statistiques.
Réponse
{"message":"deleted"}
Retourne les clics par jour, par pays, par appareil, par navigateur 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
{"slug":"abc123","total_clicks":142,"daily":[{"date":"2024-01-15","clicks":12},{"date":"2024-01-16","clicks":8}],"by_country":[{"country":"FR","clicks":98},{"country":"BE","clicks":22}],"by_device":[{"device":"mobile","clicks":91}],"by_browser":[{"browser":"Chrome","clicks":74}],"by_city":[{"city":"Paris","clicks":45},{"city":"Lyon","clicks":18}]}
Retourne les leads collectés via une squeeze page (si activée).
Réponse
{"data":[{"email":"contact@example.com","first_name":"Marie","last_name":"Dupont","created_at":"2024-01-15T14:22:00Z"}],"meta":{"current_page":1,"total":34}}
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é.
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.
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.
/.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": ["/*"]
}
]
}
}
/.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.
/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 }
/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.
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.
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)
/api/v2/zapier/triggers/qr-code-created
Dernier QR code créé — Zapier échantillonne pour la configuration du Zap. Scope : assets:read
/api/v2/zapier/triggers/short-link-created
Dernier lien court créé. Scope : assets:read
Actions
/api/v2/zapier/actions/create-qr-code
Crée un QR code depuis un Zap. Scope : assets:write
/api/v2/zapier/actions/create-short-link
Crée un lien court depuis un Zap. Scope : assets:write
REST Hooks lifecycle
/api/v2/zapier/subscribe
Zapier appelle cet endpoint pour s'abonner aux événements. Scope : webhooks:manage
/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
/api/webhooks/shopify/{integrationId}
Nouveaux produits, commandes confirmées → QR code / lien court automatique. Signature HMAC-SHA256 X-Shopify-Hmac-SHA256.
/api/webhooks/calcom/{integrationId}
Nouveau RDV confirmé → lien court vers la page de visio. Signature : Authorization Bearer partagé.
/api/webhooks/tally/{integrationId}
Soumission de formulaire → enregistrement de lead ou création de lien. Signature : X-Tally-Signature.
/api/webhooks/brevo/{integrationId}
Événements email (delivered, opened, clicked) → mise à jour du statut dans les analytics.
/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.
OK
Requête traitée avec succès. La ressource est retournée dans le corps.
Accepted
Modification soumise pour approbation (change control actif). Corps : { data: { change_request_id, status: 'pending_approval', ... } }
Unauthorized
Token manquant ou invalide.
Forbidden
Vous n'êtes pas propriétaire de la ressource, ou cette fonctionnalité n'est pas disponible sur votre plan.
Not Found
Ressource introuvable.
Unprocessable Entity
Erreur de validation — voir le champ errors.
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.
Locked
Asset gelé. Contactez le propriétaire de l'espace pour le dégeler avant toute modification.
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.
-
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}etGET /short-links/{id}pour récupérer un asset individuel sans lister. Adressage par ID entier (non slug).
-
nouveau
Change Control — workflow d'approbation
PATCH /short-links/{slug} et PATCH /qr-codes/{id} retournent 202 Accepted +
change_request_idquand l'espace a des collaborateurs et quedestination_urlest 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ètresstart_date/end_datedisponibles. -
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_atetstatus(active / expired / pending_approval). -
amélioration
Paramètre per_page sur tous les endpoints de liste
Toutes les listes acceptent désormais
per_pagejusqu'à 100 résultats (défaut 50). Réponse unifiée avec blocmetapour 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 →