v1 · stable

    API Marina

    Endpoint REST pour générer un rapport d'audit SEO + GEO + IA en marque blanche, depuis n'importe quel site, en ~3 minutes. Conçu pour être intégré en lead-magnet sur une landing page externe, depuis un agent IA, ou via un workflow no-code (Make, n8n, Zapier).

    1. Quickstart

    Trois appels suffisent : (1) obtenir une clé depuis le dashboard, (2) POST avec une URL cible, (3) polling GET jusqu'à status: "completed".

    # 1. Récupérez votre clé depuis Console → API → Marina
    export MARINA_KEY="marina_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    
    
    # 2. Lancez un audit
    JOB=$(curl -s -X POST https://tutlimtasnjabdfhpewu.functions.supabase.co/marina \
      -H "x-marina-key: $MARINA_KEY" \
      -H "Content-Type: application/json" \
      -d '{"url":"https://example.com","lang":"fr"}' | jq -r .job_id)
    
    # 3. Polling toutes les 5s jusqu'à completion
    while true; do
      R=$(curl -s "https://tutlimtasnjabdfhpewu.functions.supabase.co/marina?job_id=$JOB" -H "x-marina-key: $MARINA_KEY")
      echo "$R" | jq .status
      echo "$R" | jq -e '.status == "completed"' >/dev/null && break
      sleep 5
    done

    2. Authentification

    Toutes les requêtes externes utilisent le header x-marina-key. La clé est liée à votre compte Crawlers et hérite de votre plan (quotas, branding, langues disponibles). Deux variantes sont également acceptées : le champ api_key dans le body JSON, et un Authorization: Bearer <JWT> pour un utilisateur connecté au dashboard.

    POST /functions/v1/marina HTTP/1.1
    Host: tutlimtasnjabdfhpewu.functions.supabase.co
    x-marina-key: marina_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    Content-Type: application/json

    Générer / régénérer une clé : Console → API → Marina → Générer une clé (équivalent technique : GET ?action=generate_key avec le JWT utilisateur, et non une clé Marina). La régénération invalide immédiatement l'ancienne clé.

    3. Créer un rapport

    POST
    https://tutlimtasnjabdfhpewu.functions.supabase.co/marina

    Body

    ChampTypeRequisDescription
    urlstringouiURL à auditer (http ou https, page d'accueil recommandée).
    lang"fr" | "en" | "es"nonForce la langue du rapport. Auto-détectée si absent.
    callback_urlstring (URL)nonWebhook appelé en POST à la fin du job (voir §5).

    Réponse 200 — Job créé

    {
      "job_id": "8f3a9b2c-1234-5678-90ab-cdef01234567",
      "status": "pending"          // ou "queued" si un autre job est en cours
      // "queue_position": 2       // présent uniquement si status === "queued"
    }

    4. Récupérer le résultat

    GET
    https://tutlimtasnjabdfhpewu.functions.supabase.co/marina?job_id={JOB_ID}

    Réponses possibles

    En cours

    {
      "status": "pending" | "processing",
      "progress": 42,                // pourcentage 0-100
      "phase": "audit_seo" | "cocoon" | "geo" | "rendering"
    }

    Terminé

    {
      "success": true,
      "status": "completed",
      "data": {
        "url": "https://example.com",
        "domain": "example.com",
        "language": "fr",
        "report_url": "https://.../rapport.html",          // URL signée du rapport HTML
        "report_view_url": "https://crawlers.fr/r/8f3a9b2c-...", // lecteur public
        "report_path": "reports/8f3a9b2c/rapport.html",
        "expert_seo_score": 78,
        "expert_seo_max": 100,
        "strategic_score": 64,                              // couche stratégique GEO / IA
        "cocoon_nodes": 42,
        "cocoon_clusters": 6,
        "visual_capture": { "desktop": "https://...", "mobile": "https://..." },
        "partial": false,
        "degraded_reasons": [],
        "generated_at": "2026-05-26T14:32:11Z"
      }
    }

    Livraison partielle

    Le rapport est livré mais une couche non bloquante (le plus souvent la couche stratégique GEO) est indisponible. Traitez ce cas comme un succès exploitable.

    {
      "success": true,
      "status": "partial",
      "warning": "Couche stratégique indisponible",
      "data": { "...": "identique à completed", "partial": true, "degraded_reasons": ["strategic_timeout"] }
    }

    Échec

    {
      "success": false,
      "status": "failed",
      "error": "Crawl timeout after 180s"
    }

    Polling recommandé : toutes les 5 à 10 secondes. Durée typique d'un job : 2 à 4 minutes. Au-delà de 10 min sans complétion, le job est marqué failed automatiquement.

    5. Webhook callback (optionnel)

    Plutôt que de poller, fournissez un callback_url à la création du job. Marina enverra un POST JSON à cette URL à la fin du job (succès, livraison partielle ou échec). Le nom de l'événement est répété dans le header x-marina-event.

    POST {callback_url}
    Content-Type: application/json
    x-marina-event: marina.report.completed   // ou marina.report.partial | marina.report.failed
    
    // Succès (ou partial) — le corps reprend exactement l'objet "data" du polling
    {
      "event": "marina.report.completed",
      "job_id": "8f3a9b2c-...",
      "url": "https://example.com",
      "domain": "example.com",
      "language": "fr",
      "report_url": "https://.../rapport.html",
      "report_view_url": "https://crawlers.fr/r/8f3a9b2c-...",
      "expert_seo_score": 78,
      "expert_seo_max": 100,
      "strategic_score": 64,
      "partial": false,
      "generated_at": "2026-05-26T14:32:11Z"
    }
    
    // Échec
    {
      "event": "marina.report.failed",
      "job_id": "8f3a9b2c-...",
      "url": "https://example.com",
      "domain": "example.com",
      "phase": "phase2",
      "error": "Crawl timeout",
      "failed_at": "2026-05-26T14:32:11Z"
    }

    Le webhook est appelé une seule fois, sans retry. Votre endpoint doit répondre 2xx en moins de 10 secondes.

    6. Langues

    Marina supporte FR, EN, ES. Si lang n'est pas fourni, l'algorithme analyse <title>, <meta description>, H1/H2 puis l'attribut <html lang> en dernier recours. Le fallback final est fr.

    7. Lister / annuler / supprimer un job

    Lister vos jobs récents

    curl -X POST https://tutlimtasnjabdfhpewu.functions.supabase.co/marina \
      -H "x-marina-key: $MARINA_KEY" \
      -H "Content-Type: application/json" \
      -d '{"action":"list_jobs","limit":50}'

    Annuler un job en cours

    curl -X POST https://tutlimtasnjabdfhpewu.functions.supabase.co/marina \
      -H "x-marina-key: $MARINA_KEY" \
      -H "Content-Type: application/json" \
      -d '{"action":"cancel_job","job_id":"8f3a9b2c-..."}'

    Supprimer un job terminé

    curl -X POST https://tutlimtasnjabdfhpewu.functions.supabase.co/marina \
      -H "x-marina-key: $MARINA_KEY" \
      -H "Content-Type: application/json" \
      -d '{"action":"delete_job","job_id":"8f3a9b2c-..."}'

    8. Codes d'erreur

    CodeCasAction
    400url manquante ou callback_url invalideCorriger le body.
    401x-marina-key absent ou invalideVérifier la clé dans Console → API.
    404job_id introuvable (poll)Vérifier l'ID retourné par le POST initial.
    409Un job est déjà en cours sur ce domaineLe job est mis en file (status "queued") plutôt que rejeté.
    500Erreur interne pipelineRéessayer ; si persistant, contacter le support.

    9. Exemple complet TypeScript

    // marina.ts — client minimal, zéro dépendance
    const MARINA_KEY = process.env.MARINA_KEY!;
    const BASE = "https://tutlimtasnjabdfhpewu.functions.supabase.co/marina";
    
    export async function auditWithMarina(url: string, lang?: "fr" | "en" | "es") {
      // 1. Créer le job
      const create = await fetch(BASE, {
        method: "POST",
        headers: { "x-marina-key": MARINA_KEY, "Content-Type": "application/json" },
        body: JSON.stringify({ url, lang }),
      });
      if (!create.ok) throw new Error(`Create failed: ${create.status}`);
      const { job_id } = await create.json();
    
      // 2. Poll toutes les 5s, timeout 10 min
      const deadline = Date.now() + 10 * 60_000;
      while (Date.now() < deadline) {
        await new Promise((r) => setTimeout(r, 5000));
        const poll = await fetch(`${BASE}?job_id=${job_id}`, {
          headers: { "x-marina-key": MARINA_KEY },
        });
        const json = await poll.json();
        if (json.status === "completed" || json.status === "partial") return json.data; // { report_url, expert_seo_score, strategic_score, ... }
        if (json.status === "failed") throw new Error(json.error);
      }
      throw new Error("Timeout");
    }
    
    // Usage
    const report = await auditWithMarina("https://example.com", "fr");
    console.log(report.report_url);

    10. Intégration depuis Lovable / Edge Function

    Stockez MARINA_KEY en secret, puis appelez l'API depuis une edge function. Exemple Deno :

    // supabase/functions/audit-prospect/index.ts
    import { corsHeaders } from "../_shared/cors.ts";
    
    Deno.serve(async (req) => {
      if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders });
      const { url } = await req.json();
    
      const res = await fetch("https://tutlimtasnjabdfhpewu.functions.supabase.co/marina", {
        method: "POST",
        headers: {
          "x-marina-key": Deno.env.get("MARINA_KEY")!,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          url,
          lang: "fr",
          callback_url: `${Deno.env.get("SUPABASE_URL")}/functions/v1/marina-webhook`,
        }),
      });
    
      const { job_id } = await res.json();
      return new Response(JSON.stringify({ job_id }), {
        headers: { ...corsHeaders, "Content-Type": "application/json" },
      });
    });

    11. Carte d'identité (phase 0, pré-crawl)

    Avant de lancer un audit, Marina résout une carte d'identité du site (secteur, modèle commercial, cible, offre, zone commerciale). Elle calibre les seuils de sévérité et les cibles de mix de gabarits. Vous pouvez la lire, la recalculer avec vos propres valeurs, puis la verrouiller pour qu'elle soit utilisée par l'audit.

    actionBodyEffet
    identity_optionsListes de référence (secteurs, modèles commerciaux, types d'entité).
    identity_resolveurl, force?Résolution automatique (cache, sinon inférence). Réponse { card, locked, options }.
    identity_recomputeurl, fieldsPrévisualisation déterministe (aucune écriture) avec vos champs.
    identity_lockurl, fieldsPersiste et verrouille la carte : l'audit l'utilisera telle quelle.
    curl -X POST https://tutlimtasnjabdfhpewu.functions.supabase.co/marina \
      -H "x-marina-key: $MARINA_KEY" -H "Content-Type: application/json" \
      -d '{"action":"identity_lock","url":"https://example.com","fields":{
            "sector":"renovation_batiment",
            "commercialModel":"local_service",
            "productsServices":"Rénovation complète, isolation, toiture",
            "targetAudience":"Propriétaires particuliers",
            "commercialArea":"Provence"}}'

    Le verrouillage nécessite que le domaine soit suivi par votre compte ; sinon la carte est renvoyée en prévisualisation seulement.

    12. Multipages

    Il n'existe pas d'action batch côté API : un audit multipages est une boucle client sur l'endpoint standard, une URL par job (jusqu'à 15 URLs dans l'interface Marina, 5 crédits par rapport). Espacez les créations de jobs d'environ 20 secondes ; les jobs du même domaine sont mis en file et partagent le crawl en cours plutôt que de le relancer.

    Besoin d'aller plus loin ? Consultez la liste de toutes les intégrations API ou contactez le support.