openapi: 3.0.3 info: title: 'Fassiki SaaS API Documentation' description: '' version: 1.0.0 servers: - url: 'https://fassiki.com' tags: - name: Analytics description: '' - name: Audit description: '' - name: Compte description: '' - name: Endpoints description: '' - name: Pusher description: '' - name: Uptime description: '' components: securitySchemes: default: type: http scheme: bearer description: "Récupérez votre clé API dans votre espace utilisateur → **Mon compte**. Vous pouvez l'envoyer via l'en-tête `Authorization: Bearer ` **ou** `X-API-Key: `." security: - default: [] paths: /api/v1/analytics/websites: get: summary: 'Lister les sites analytics' operationId: listerLesSitesAnalytics description: "Renvoie tous les sites web analytics de l'utilisateur authentifié, avec le nombre de visiteurs." parameters: [] responses: 200: description: Liste content: application/json: schema: type: object example: data: - id: 1 name: 'Mon site' host: example.com tracking_type: normal visitors_count: 0 is_enabled: true properties: data: type: array example: - id: 1 name: 'Mon site' host: example.com tracking_type: normal visitors_count: 0 is_enabled: true description: 'Liste des sites.' items: type: object properties: id: type: integer example: 1 name: type: string example: 'Mon site' host: type: string example: example.com tracking_type: type: string example: normal visitors_count: type: integer example: 0 is_enabled: type: boolean example: true tags: - Analytics post: summary: 'Créer un site analytics' operationId: crerUnSiteAnalytics description: "Crée un nouveau site web à tracker. Une `pixel_key` est générée automatiquement\npour intégrer le script de tracking. Le plan doit avoir le module Analytics activé\net ne pas avoir atteint sa limite de sites." parameters: [] responses: 201: description: 'Site créé' content: application/json: schema: type: object example: id: 2 name: 'Mon blog' host: example.com pixel_key: aB3x... tracking_type: normal is_enabled: true properties: id: type: integer example: 2 name: type: string example: 'Mon blog' host: type: string example: example.com pixel_key: type: string example: aB3x... tracking_type: type: string example: normal is_enabled: type: boolean example: true 402: description: 'Limite atteinte' content: application/json: schema: type: object example: message: 'Limite de sites atteinte pour votre plan (1).' properties: message: type: string example: 'Limite de sites atteinte pour votre plan (1).' 403: description: 'Module désactivé' content: application/json: schema: type: object example: message: "Votre plan ne permet pas d'accéder à cette ressource." properties: message: type: string example: "Votre plan ne permet pas d'accéder à cette ressource." tags: - Analytics requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Nom du site (max 64).' example: 'Mon blog' host: type: string description: 'Domaine/hôte du site (max 255).' example: example.com tracking_type: type: string description: 'Mode de suivi : `normal` (cookies) ou `lightweight` (sans cookies).' example: normal required: - name - host '/api/v1/analytics/websites/{id}': get: summary: 'Afficher un site analytics' operationId: afficherUnSiteAnalytics description: "Renvoie les détails d'un site analytics spécifique appartenant à l'utilisateur." parameters: [] responses: 200: description: 'Site trouvé' content: application/json: schema: type: object example: id: 1 name: 'Mon site' host: example.com tracking_type: normal properties: id: type: integer example: 1 name: type: string example: 'Mon site' host: type: string example: example.com tracking_type: type: string example: normal 404: description: Introuvable content: application/json: schema: type: object example: message: 'Ressource introuvable.' properties: message: type: string example: 'Ressource introuvable.' tags: - Analytics put: summary: 'Mettre à jour un site analytics' operationId: mettreJourUnSiteAnalytics description: "Met à jour les champs fournis d'un site analytics." parameters: [] responses: 200: description: 'Site mis à jour' content: application/json: schema: type: object example: id: 1 name: 'Nouveau nom' host: newdomain.com is_enabled: true properties: id: type: integer example: 1 name: type: string example: 'Nouveau nom' host: type: string example: newdomain.com is_enabled: type: boolean example: true tags: - Analytics requestBody: required: false content: application/json: schema: type: object properties: name: type: string description: 'parfois Nom du site.' example: 'Nouveau nom' host: type: string description: 'parfois Domaine/hôte.' example: newdomain.com tracking_type: type: string description: '' example: normal enum: - normal - lightweight is_enabled: type: boolean description: 'parfois Activer/désactiver le suivi.' example: false delete: summary: 'Supprimer un site analytics' operationId: supprimerUnSiteAnalytics description: 'Supprime définitivement un site analytics et toutes ses données associées.' parameters: [] responses: 204: description: 'Site supprimé' content: text/plain: schema: type: string example: '' 404: description: Introuvable content: application/json: schema: type: object example: message: 'Ressource introuvable.' properties: message: type: string example: 'Ressource introuvable.' tags: - Analytics parameters: - in: path name: id description: 'The ID of the website.' example: architecto required: true schema: type: string - in: path name: website description: 'Identifiant du site.' example: 1 required: true schema: type: integer /api/v1/audits: get: summary: 'Lister les audits' operationId: listerLesAudits description: "Renvoie tous les audits de sites web de l'utilisateur, triés par date." parameters: [] responses: 200: description: Liste content: application/json: schema: type: object example: data: - id: 1 url: 'https://example.com' host: example.com score: 78 total_tests: 22 passed_tests: 17 properties: data: type: array example: - id: 1 url: 'https://example.com' host: example.com score: 78 total_tests: 22 passed_tests: 17 items: type: object properties: id: type: integer example: 1 url: type: string example: 'https://example.com' host: type: string example: example.com score: type: integer example: 78 total_tests: type: integer example: 22 passed_tests: type: integer example: 17 tags: - Audit post: summary: 'Créer et lancer un audit' operationId: crerEtLancerUnAudit description: "Crée un audit pour l'URL fournie **et le lance immédiatement en arrière-plan**\n(tests SEO, performance, sécurité, accessibilité). Si l'IA est activée, un résumé\nIA est également demandé. Le score et les détails sont remplis après exécution\n(récupérables via `GET /audits/{id}`). Le plan doit avoir le module Audit activé." parameters: [] responses: 201: description: 'Audit lancé' content: application/json: schema: type: object example: audit: id: 1 url: 'https://example.com' host: example.com type: single is_queued: true message: 'Audit lancé en arrière-plan. Le score sera disponible sous peu.' properties: audit: type: object properties: id: type: integer example: 1 url: type: string example: 'https://example.com' host: type: string example: example.com type: type: string example: single is_queued: type: boolean example: true message: type: string example: 'Audit lancé en arrière-plan. Le score sera disponible sous peu.' 402: description: 'Limite atteinte' content: application/json: schema: type: object example: message: "Limite d'audits mensuels atteinte (100)." properties: message: type: string example: "Limite d'audits mensuels atteinte (100)." tags: - Audit requestBody: required: true content: application/json: schema: type: object properties: url: type: string description: 'URL du site à auditer.' example: 'https://example.com' type: type: string description: 'Mode : `single`, `sitemap`, `bulk` ou `html`.' example: single required: - url '/api/v1/audits/{audit}/run': post: summary: 'Relancer un audit (refresh)' operationId: relancerUnAuditrefresh description: "Relance un audit existant : l'ancien résultat est archivé, un nouveau score est\ncalculé, et un résumé IA est régénéré si l'IA est activée." parameters: [] responses: 200: description: 'Audit relancé' content: application/json: schema: type: object example: message: 'Audit relancé.' audit_id: 1 properties: message: type: string example: 'Audit relancé.' audit_id: type: integer example: 1 tags: - Audit parameters: - in: path name: audit description: "Identifiant de l'audit." example: 1 required: true schema: type: integer /api/v1/user: get: summary: 'Profil & formule' operationId: profilFormule description: "Renvoie le profil de l'utilisateur authentifié ainsi que sa formule (plan) et\nles modules auxquels il a accès. Idéal pour vérifier qu'une clé API est valide." parameters: [] responses: 200: description: 'Profil récupéré' content: application/json: schema: type: object example: id: 1 name: Administrateur email: admin@fassiki.com locale: fr plan: name: Business slug: business modules: analytics: true pusher: true uptime: true audit: true plan_expiration_date: null has_active_subscription: false properties: id: type: integer example: 1 description: "Identifiant de l'utilisateur." name: type: string example: Administrateur description: 'Nom complet.' email: type: string example: admin@fassiki.com description: 'Adresse email.' locale: type: string example: fr plan: type: object properties: name: type: string example: Business slug: type: string example: business modules: type: object properties: analytics: type: boolean example: true pusher: type: boolean example: true uptime: type: boolean example: true audit: type: boolean example: true description: 'Formule souscrite (name, slug, modules activés).' plan_expiration_date: type: string example: null nullable: true has_active_subscription: type: boolean example: false description: "Présence d'un abonnement payant actif." tags: - Compte /api/v1/ping: get: summary: '' operationId: getApiV1Ping description: '' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: name: 'Fassiki SaaS' version: v1 time: '2026-07-30T10:33:09+00:00' properties: name: type: string example: 'Fassiki SaaS' version: type: string example: v1 time: type: string example: '2026-07-30T10:33:09+00:00' tags: - Endpoints '/api/v1/pusher/campaigns/{id}': get: summary: 'GET /api/v1/pusher/campaigns/{campaign}' operationId: gETapiv1pushercampaignscampaign description: '' parameters: [] responses: 500: description: '' content: application/json: schema: type: object example: message: 'Server Error' properties: message: type: string example: 'Server Error' tags: - Endpoints put: summary: 'PUT /api/v1/pusher/campaigns/{campaign}' operationId: pUTapiv1pushercampaignscampaign description: '' parameters: [] responses: { } tags: - Endpoints requestBody: required: false content: application/json: schema: type: object properties: name: type: string description: 'Must not be greater than 64 characters.' example: b title: type: string description: 'Must not be greater than 128 characters.' example: 'n' description: type: string description: '' example: 'Eius et animi quos velit et.' nullable: true url: type: string description: 'Must be a valid URL.' example: 'http://www.ernser.org/harum-mollitia-modi-deserunt-aut-ab-provident-perspiciatis-quo.html' nullable: true image: type: string description: 'Must be a valid URL.' example: 'http://swift.com/quidem-nostrum-qui-commodi-incidunt-iure-odit.html' nullable: true status: type: string description: '' example: sending enum: - draft - scheduled - sending - sent - failed delete: summary: 'DELETE /api/v1/pusher/campaigns/{campaign}' operationId: dELETEapiv1pushercampaignscampaign description: '' parameters: [] responses: { } tags: - Endpoints parameters: - in: path name: id description: 'The ID of the campaign.' example: architecto required: true schema: type: string '/api/v1/uptime/monitors/{id}': get: summary: 'GET /api/v1/uptime/monitors/{monitor}' operationId: gETapiv1uptimemonitorsmonitor description: '' parameters: [] responses: 500: description: '' content: application/json: schema: type: object example: message: 'Server Error' properties: message: type: string example: 'Server Error' tags: - Endpoints put: summary: 'PUT /api/v1/uptime/monitors/{monitor}' operationId: pUTapiv1uptimemonitorsmonitor description: '' parameters: [] responses: { } tags: - Endpoints requestBody: required: false content: application/json: schema: type: object properties: name: type: string description: 'Must not be greater than 64 characters.' example: b type: type: string description: '' example: port enum: - website - port - ping target: type: string description: 'Must not be greater than 255 characters.' example: 'n' port: type: integer description: '' example: 16 nullable: true is_enabled: type: boolean description: '' example: true settings: type: object description: '' example: null properties: { } delete: summary: 'DELETE /api/v1/uptime/monitors/{monitor}' operationId: dELETEapiv1uptimemonitorsmonitor description: '' parameters: [] responses: { } tags: - Endpoints parameters: - in: path name: id description: 'The ID of the monitor.' example: architecto required: true schema: type: string '/api/v1/audits/{id}': get: summary: 'GET /api/v1/audits/{audit}' operationId: gETapiv1auditsaudit description: '' parameters: [] responses: 500: description: '' content: application/json: schema: type: object example: message: 'Server Error' properties: message: type: string example: 'Server Error' tags: - Endpoints put: summary: 'PUT /api/v1/audits/{audit}' operationId: pUTapiv1auditsaudit description: '' parameters: [] responses: { } tags: - Endpoints requestBody: required: false content: application/json: schema: type: object properties: is_public: type: boolean description: '' example: false password: type: string description: '' example: '|]|{+-' nullable: true is_archived: type: boolean description: '' example: false delete: summary: 'DELETE /api/v1/audits/{audit}' operationId: dELETEapiv1auditsaudit description: '' parameters: [] responses: { } tags: - Endpoints parameters: - in: path name: id description: 'The ID of the audit.' example: architecto required: true schema: type: string /api/v1/pusher/campaigns: get: summary: 'Lister les campagnes push' operationId: listerLesCampagnesPush description: "Renvoie toutes les campagnes de notifications push de l'utilisateur, triées par date." parameters: [] responses: 200: description: Liste content: application/json: schema: type: object example: data: - id: 1 name: 'Promo été' title: "-20% aujourd'hui" status: draft website: name: 'Mon site' host: example.com properties: data: type: array example: - id: 1 name: 'Promo été' title: "-20% aujourd'hui" status: draft website: name: 'Mon site' host: example.com items: type: object properties: id: type: integer example: 1 name: type: string example: 'Promo été' title: type: string example: "-20% aujourd'hui" status: type: string example: draft website: type: object properties: name: type: string example: 'Mon site' host: type: string example: example.com tags: - Pusher post: summary: 'Créer une campagne push' operationId: crerUneCampagnePush description: "Crée une nouvelle campagne de notifications push (au statut `draft`). Elle pourra\nensuite être planifiée/envoyée depuis l'interface ou via le cron `pusher:process-scheduled`." parameters: [] responses: 201: description: 'Campagne créée' content: application/json: schema: type: object example: id: 1 name: 'Promo été' title: "-20% aujourd'hui" status: draft properties: id: type: integer example: 1 name: type: string example: 'Promo été' title: type: string example: "-20% aujourd'hui" status: type: string example: draft 403: description: 'Module désactivé' content: application/json: schema: type: object example: message: "Votre plan ne permet pas d'accéder à cette ressource." properties: message: type: string example: "Votre plan ne permet pas d'accéder à cette ressource." tags: - Pusher requestBody: required: true content: application/json: schema: type: object properties: website_id: type: integer description: 'Identifiant du site push.' example: 1 name: type: string description: 'Nom interne de la campagne.' example: 'Promo été' title: type: string description: 'Titre de la notification.' example: "-20% aujourd'hui" description: type: string description: 'Message de la notification.' example: 'Eius et animi quos velit et.' nullable: true url: type: string description: "URL d'ouverture au clic." example: 'http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html' nullable: true image: type: string description: 'Must be a valid URL.' example: 'http://swift.com/quidem-nostrum-qui-commodi-incidunt-iure-odit.html' nullable: true required: - website_id - name - title /api/v1/uptime/monitors: get: summary: 'Lister les moniteurs' operationId: listerLesMoniteurs description: "Renvoie tous les moniteurs (HTTP/HTTPS, port, ping) de l'utilisateur avec leurs\nstatistiques d'uptime et de latence." parameters: [] responses: 200: description: Liste content: application/json: schema: type: object example: data: - id: 1 name: 'Mon site' type: website target: 'https://example.com' is_ok: true uptime: 100 average_response_time: 0.42 properties: data: type: array example: - id: 1 name: 'Mon site' type: website target: 'https://example.com' is_ok: true uptime: 100 average_response_time: 0.42 items: type: object properties: id: type: integer example: 1 name: type: string example: 'Mon site' type: type: string example: website target: type: string example: 'https://example.com' is_ok: type: boolean example: true uptime: type: integer example: 100 average_response_time: type: number example: 0.42 tags: - Uptime post: summary: 'Créer un moniteur' operationId: crerUnMoniteur description: "Crée un nouveau moniteur. Le prochain check est planifié automatiquement selon\nl'intervalle configuré (`settings.interval`, défaut 5 minutes). Le plan doit avoir\nle module Uptime activé et ne pas avoir atteint sa limite de moniteurs." parameters: [] responses: 201: description: 'Moniteur créé' content: application/json: schema: type: object example: id: 1 name: 'Mon site' type: website target: 'https://example.com' is_ok: true next_check_datetime: '2026-07-30T10:26:14.000000Z' properties: id: type: integer example: 1 name: type: string example: 'Mon site' type: type: string example: website target: type: string example: 'https://example.com' is_ok: type: boolean example: true next_check_datetime: type: string example: '2026-07-30T10:26:14.000000Z' 402: description: 'Limite atteinte' content: application/json: schema: type: object example: message: 'Limite de moniteurs atteinte pour votre plan (50).' properties: message: type: string example: 'Limite de moniteurs atteinte pour votre plan (50).' tags: - Uptime requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Nom du moniteur.' example: 'Mon site' type: type: string description: 'Type : `website`, `port` ou `ping`.' example: website target: type: string description: 'URL ou hôte à surveiller.' example: 'https://example.com' port: type: integer description: 'Port (pour le type `port`).' example: 16 nullable: true is_enabled: type: boolean description: '' example: false settings: type: object description: 'Réglages avancés (interval, timeout, expected_code, keyword).' example: [] properties: { } required: - name - target '/api/v1/uptime/monitors/{monitor}/check': post: summary: 'Lancer une vérification immédiate' operationId: lancerUneVrificationImmdiate description: "Déclenche un check manuel du moniteur (mis en file d'attente). Le résultat\n(uptime, latence, incident éventuel) sera mis à jour après exécution." parameters: [] responses: 200: description: 'Check lancé' content: application/json: schema: type: object example: message: 'Vérification lancée.' monitor_id: 1 properties: message: type: string example: 'Vérification lancée.' monitor_id: type: integer example: 1 tags: - Uptime parameters: - in: path name: monitor description: 'Identifiant du moniteur.' example: 1 required: true schema: type: integer