AnyCrawl

Webhooks

Recevez des notifications en temps réel pour tous les événements AnyCrawl : scraping, crawling, map, recherche et tâches planifiées.

Introduction

Les webhooks vous permettent de recevoir des notifications HTTP en temps réel lorsque des événements se produisent sur votre compte AnyCrawl. Sans polling, AnyCrawl envoie automatiquement des requêtes POST vers votre point de terminaison lorsque des événements surviennent.

Points clés : abonnement à plusieurs types d’événements, vérification de signature HMAC-SHA256, nouvelles tentatives automatiques avec backoff exponentiel, historique des livraisons, filtrage de portée, en-têtes HTTP personnalisés et protection contre les IP privées (SSRF).

Fonctionnalités principales

  • Abonnements : scraping, crawling, map, recherche, tâches planifiées et événements système
  • Livraison sécurisée : signature HMAC-SHA256
  • Nouvelles tentatives : backoff exponentiel en cas d’échec
  • Suivi des livraisons : historique complet
  • Filtrage de portée : tous les événements ou tâches spécifiques
  • En-têtes personnalisés : en-têtes HTTP supplémentaires pour les requêtes webhook
  • Protection IP privée : limitation des attaques SSRF

Points de terminaison API

POST   /v1/webhooks                              # Créer un abonnement webhook
GET    /v1/webhooks                              # Lister les webhooks
GET    /v1/webhooks/:webhookId                   # Détails d’un webhook
PUT    /v1/webhooks/:webhookId                   # Mettre à jour un webhook
DELETE /v1/webhooks/:webhookId                   # Supprimer un webhook
GET    /v1/webhooks/:webhookId/deliveries        # Historique des livraisons
POST   /v1/webhooks/:webhookId/test              # Envoyer un webhook de test
PUT    /v1/webhooks/:webhookId/activate          # Activer un webhook
PUT    /v1/webhooks/:webhookId/deactivate        # Désactiver un webhook
POST   /v1/webhooks/:webhookId/deliveries/:deliveryId/replay  # Relancer une livraison échouée
GET    /v1/webhook-events                        # Lister les événements pris en charge

Événements pris en charge

Événements de jobs

ÉvénementDescriptionDéclenché lorsque
scrape.createdJob de scrape crééNouveau job de scrape en file
scrape.startedJob de scrape démarréLe job commence
scrape.completedJob de scrape terminéSuccès
scrape.failedJob de scrape en échecErreur
scrape.cancelledJob de scrape annuléAnnulation manuelle
crawl.createdJob de crawl crééNouveau job de crawl en file
crawl.startedJob de crawl démarréLe job commence
crawl.completedJob de crawl terminéSuccès
crawl.failedJob de crawl en échecErreur
crawl.cancelledJob de crawl annuléAnnulation manuelle

Événements de tâches planifiées

ÉvénementDescriptionDéclenché lorsque
task.executedTâche exécutéeLa tâche planifiée s’exécute
task.failedTâche en échecÉchec de la tâche planifiée
task.pausedTâche mise en pauseLa tâche est en pause
task.resumedTâche repriseLa tâche reprend
ÉvénementDescriptionDéclenché lorsque
search.createdJob Search crééNouveau job Search en file
search.startedJob Search démarréLe job commence
search.completedJob Search terminéSuccès
search.failedJob Search en échecErreur

Événements Map

ÉvénementDescriptionDéclenché lorsque
map.createdJob Map crééNouveau job Map en file
map.startedJob Map démarréLe job commence
map.completedJob Map terminéSuccès
map.failedJob Map en échecErreur

Événements de test

ÉvénementDescriptionDéclenché lorsque
webhook.testÉvénement de testEnvoi manuel d’un webhook de test

Démarrage rapide

Créer un webhook

curl -X POST "https://api.anycrawl.dev/v1/webhooks" \
  -H "Authorization: Bearer <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production Notifications",
    "webhook_url": "https://your-domain.com/webhooks/anycrawl",
    "event_types": ["scrape.completed", "scrape.failed", "crawl.completed"],
    "scope": "all",
    "timeout_seconds": 10,
    "max_retries": 3
  }'

Réponse

{
  "success": true,
  "data": {
    "webhook_id": "webhook-uuid-here",
    "secret": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6",
    "message": "Webhook created successfully. Save the secret - it won't be shown again."
  }
}

Important : enregistrez immédiatement le secret ! Il n’est affiché qu’une fois à la création et est nécessaire pour vérifier la signature.

Paramètres de requête

Configuration du webhook

ParamètreTypeObligatoireDéfautDescription
namestringOui-Nom du webhook (1 à 255 caractères)
descriptionstringNon-Description du webhook
webhook_urlstringOui-URL de votre point de terminaison (HTTPS recommandé)
event_typesstring[]Oui-Types d’événements auxquels s’abonner
scopestringNon"all"Portée : "all" ou "specific"
specific_task_idsstring[]Non-IDs de tâches (si scope = "specific")

Configuration de livraison

ParamètreTypeObligatoireDéfautDescription
timeout_secondsnumberNon10Délai d’attente de la requête (1 à 60 s)
max_retriesnumberNon3Nombre max de tentatives (0 à 10)
retry_backoff_multipliernumberNon2Multiplicateur de backoff (1 à 10)
custom_headersobjectNon-En-têtes HTTP personnalisés

Les webhooks sont désactivés automatiquement après 10 échecs consécutifs pour limiter les nouvelles tentatives excessives. Vous pouvez les réactiver manuellement après correction.

Métadonnées

ParamètreTypeObligatoireDéfautDescription
tagsstring[]Non-Étiquettes pour l’organisation
metadataobjectNon-Métadonnées personnalisées

Format de charge utile webhook

En-têtes HTTP

Chaque requête webhook inclut ces en-têtes :

Content-Type: application/json
X-AnyCrawl-Signature: sha256=abc123...
X-Webhook-Event: scrape.completed
X-Webhook-Delivery-Id: delivery-uuid-1
X-Webhook-Timestamp: 2026-01-27T10:00:00.000Z

Exemples de charge utile

scrape.completed

{
  "job_id": "job-uuid-1",
  "status": "completed",
  "url": "https://example.com",
  "total": 10,
  "completed": 10,
  "failed": 0,
  "credits_used": 5,
  "created_at": "2026-01-27T09:00:00.000Z",
  "completed_at": "2026-01-27T10:00:00.000Z"
}

scrape.failed

{
  "job_id": "job-uuid-1",
  "status": "failed",
  "url": "https://example.com",
  "error_message": "Connection timeout",
  "credits_used": 3,
  "created_at": "2026-01-27T09:00:00.000Z",
  "completed_at": "2026-01-27T10:00:00.000Z"
}

task.executed

{
  "task_id": "task-uuid-1",
  "task_name": "Daily News Scrape",
  "execution_id": "exec-uuid-1",
  "execution_number": 45,
  "status": "completed",
  "job_id": "job-uuid-1",
  "credits_used": 5,
  "scheduled_for": "2026-01-27T09:00:00.000Z",
  "completed_at": "2026-01-27T09:02:15.000Z"
}

Vérification des signatures

Pourquoi vérifier les signatures ?

La vérification des signatures garantit que les requêtes webhook proviennent bien d’AnyCrawl et n’ont pas été altérées, ce qui protège contre les requêtes malveillantes.

Algorithme de vérification

AnyCrawl signe les charges utiles avec HMAC-SHA256 :

signature = HMAC-SHA256(payload, webhook_secret)
header_value = "sha256=" + hex(signature)

Exemples d’implémentation

Node.js / Express

const crypto = require('crypto');
const express = require('express');

function verifyWebhookSignature(payload, signature, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(JSON.stringify(payload));
  const expectedSignature = `sha256=${hmac.digest('hex')}`;

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

const app = express();
app.use(express.json());

app.post('/webhooks/anycrawl', (req, res) => {
  const signature = req.headers['x-anycrawl-signature'];
  const secret = process.env.WEBHOOK_SECRET;

  // Verify signature
  if (!verifyWebhookSignature(req.body, signature, secret)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Extract event info
  const eventType = req.headers['x-webhook-event'];
  const deliveryId = req.headers['x-webhook-delivery-id'];

  console.log(`Received event: ${eventType}`);
  console.log(`Delivery ID: ${deliveryId}`);
  console.log('Payload:', req.body);

  // Respond quickly (< 5 seconds recommended)
  res.status(200).json({ received: true });

  // Process asynchronously
  processWebhookAsync(eventType, req.body).catch(console.error);
});

app.listen(3000);

Python / Flask

import hmac
import hashlib
import json
from flask import Flask, request, jsonify

app = Flask(__name__)
WEBHOOK_SECRET = 'your-webhook-secret-here'

def verify_webhook_signature(payload, signature, secret):
    expected_signature = 'sha256=' + hmac.new(
        secret.encode('utf-8'),
        json.dumps(payload).encode('utf-8'),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(signature, expected_signature)

@app.route('/webhooks/anycrawl', methods=['POST'])
def webhook_handler():
    signature = request.headers.get('X-AnyCrawl-Signature')
    payload = request.get_json()

    # Verify signature
    if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET):
        return jsonify({'error': 'Invalid signature'}), 401

    # Extract event info
    event_type = request.headers.get('X-Webhook-Event')
    delivery_id = request.headers.get('X-Webhook-Delivery-Id')

    print(f'Received event: {event_type}')
    print(f'Delivery ID: {delivery_id}')
    print(f'Payload: {payload}')

    # Respond quickly
    return jsonify({'received': True}), 200

if __name__ == '__main__':
    app.run(port=3000)

Go

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
)

func verifyWebhookSignature(payload []byte, signature, secret string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(payload)
    expectedSignature := "sha256=" + hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(signature), []byte(expectedSignature))
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    signature := r.Header.Get("X-AnyCrawl-Signature")
    eventType := r.Header.Get("X-Webhook-Event")
    secret := os.Getenv("WEBHOOK_SECRET")

    body, err := io.ReadAll(r.Body)
    if err != nil {
        http.Error(w, "Error reading body", http.StatusBadRequest)
        return
    }

    if !verifyWebhookSignature(body, signature, secret) {
        http.Error(w, "Invalid signature", http.StatusUnauthorized)
        return
    }

    var payload map[string]interface{}
    if err := json.Unmarshal(body, &payload); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }

    fmt.Printf("Received event: %s\n", eventType)
    fmt.Printf("Payload: %+v\n", payload)

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]bool{"received": true})
}

func main() {
    http.HandleFunc("/webhooks/anycrawl", webhookHandler)
    http.ListenAndServe(":3000", nil)
}

Gérer les webhooks

Lister tous les webhooks

curl -X GET "https://api.anycrawl.dev/v1/webhooks" \
  -H "Authorization: Bearer <your-api-key>"

Réponse

{
  "success": true,
  "data": [
    {
      "uuid": "webhook-uuid-1",
      "name": "Production Notifications",
      "webhook_url": "https://your-domain.com/webhooks/anycrawl",
      "webhook_secret": "***hidden***",
      "event_types": ["scrape.completed", "scrape.failed"],
      "scope": "all",
      "is_active": true,
      "consecutive_failures": 0,
      "total_deliveries": 145,
      "successful_deliveries": 142,
      "failed_deliveries": 3,
      "last_success_at": "2026-01-27T10:00:00.000Z",
      "last_failure_at": "2026-01-26T15:30:00.000Z",
      "created_at": "2026-01-01T00:00:00.000Z"
    }
  ]
}

Le webhook_secret est toujours masqué dans les vues liste et détail pour des raisons de sécurité.

Mettre à jour un webhook

curl -X PUT "https://api.anycrawl.dev/v1/webhooks/:webhookId" \
  -H "Authorization: Bearer <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "event_types": ["scrape.completed", "scrape.failed", "crawl.completed"]
  }'

Vous ne pouvez pas modifier le secret du webhook. Pour le changer, supprimez le webhook et recréez-le.

Tester les webhooks

Envoyez un événement de test pour vérifier la configuration de votre webhook :

curl -X POST "https://api.anycrawl.dev/v1/webhooks/:webhookId/test" \
  -H "Authorization: Bearer <your-api-key>"

Charge utile de test :

{
  "message": "This is a test webhook from AnyCrawl",
  "timestamp": "2026-01-27T10:00:00.000Z",
  "webhook_id": "webhook-uuid-1"
}

Désactiver / activer un webhook

curl -X PUT "https://api.anycrawl.dev/v1/webhooks/:webhookId/deactivate" \
  -H "Authorization: Bearer <your-api-key>"

Supprimer un webhook

curl -X DELETE "https://api.anycrawl.dev/v1/webhooks/:webhookId" \
  -H "Authorization: Bearer <your-api-key>"

La suppression d’un webhook supprime également tout l’historique de livraison associé.

Relancer une livraison échouée

Relancez manuellement une livraison webhook ayant échoué :

curl -X POST "https://api.anycrawl.dev/v1/webhooks/:webhookId/deliveries/:deliveryId/replay" \
  -H "Authorization: Bearer <your-api-key>"

Réponse :

{
  "success": true,
  "message": "Delivery replayed successfully",
  "data": {
    "delivery_id": "delivery-uuid-1",
    "status": "pending"
  }
}

Relancer une livraison crée une nouvelle tentative avec la même charge utile. Utile après correction du point de terminaison pour les livraisons ayant échoué.

Historique des livraisons

Consulter les livraisons

curl -X GET "https://api.anycrawl.dev/v1/webhooks/:webhookId/deliveries?limit=20" \
  -H "Authorization: Bearer <your-api-key>"

Paramètres de requête

ParamètreTypeDéfautDescription
limitnumber100Nombre de livraisons à renvoyer
offsetnumber0Livraisons à ignorer
statusstring-Filtrer par statut : delivered, failed, retrying
fromstring-Date de début (ISO 8601)
tostring-Date de fin (ISO 8601)

Réponse

{
  "success": true,
  "data": [
    {
      "uuid": "delivery-uuid-1",
      "webhookSubscriptionUuid": "webhook-uuid-1",
      "eventType": "scrape.completed",
      "status": "delivered",
      "attempt_number": 1,
      "request_url": "https://your-domain.com/webhooks/anycrawl",
      "request_method": "POST",
      "response_status": 200,
      "response_duration_ms": 125,
      "created_at": "2026-01-27T10:00:00.000Z",
      "delivered_at": "2026-01-27T10:00:00.125Z"
    },
    {
      "uuid": "delivery-uuid-2",
      "status": "failed",
      "attempt_number": 3,
      "error_message": "Connection timeout",
      "error_code": "ETIMEDOUT",
      "created_at": "2026-01-27T09:00:00.000Z"
    }
  ],
  "meta": {
    "limit": 20,
    "offset": 0,
    "filters": {
      "status": null,
      "from": null,
      "to": null
    }
  }
}

Mécanisme de nouvelle tentative

Quand les nouvelles tentatives ont lieu

Les webhooks sont réessayés lorsque :

  • le code HTTP n’est pas 2xx
  • un délai de connexion est dépassé
  • une erreur réseau survient

Calendrier des nouvelles tentatives

Avec les paramètres par défaut (max_retries: 3, retry_backoff_multiplier: 2) :

TentativeDélaiTemps après le premier envoi
1re tentative1 minute1 minute
2e tentative2 minutes3 minutes
3e tentative4 minutes7 minutes

Formule de délai : backoff_multiplier ^ (attempt - 1) × 1 minute

Désactivation automatique

Les webhooks sont désactivés automatiquement après 10 échecs consécutifs pour limiter les nouvelles tentatives excessives.

Pour réactiver :

curl -X PUT "https://api.anycrawl.dev/v1/webhooks/:webhookId/activate" \
  -H "Authorization: Bearer <your-api-key>"

Filtrage de portée

Tous les événements (scope: "all")

Recevez des notifications pour tous les événements des types auxquels vous êtes abonné :

{
  "scope": "all",
  "event_types": ["scrape.completed", "crawl.completed"]
}

Tâches spécifiques (scope: "specific")

Recevez des notifications uniquement pour certaines tâches planifiées :

{
  "scope": "specific",
  "specific_task_ids": ["task-uuid-1", "task-uuid-2"],
  "event_types": ["task.executed", "task.failed"]
}

Protection des IP privées

Comportement par défaut

AnyCrawl bloque les livraisons de webhooks vers des adresses IP privées :

  • 10.0.0.0/8
  • 172.16.0.0/12
  • 192.168.0.0/16
  • 169.254.0.0/16 (link-local)
  • 127.0.0.1 / localhost
  • IPv6 private addresses

Autoriser les webhooks locaux (tests uniquement)

Pour le développement local, définissez :

ALLOW_LOCAL_WEBHOOKS=true

Ne l’activez jamais en production : risques de sécurité graves.

Bonnes pratiques

1. Répondre rapidement

Renvoyez un code 2xx dans les 5 secondes :

app.post('/webhook', async (req, res) => {
  // Verify signature
  if (!verifySignature(req.body, req.headers['x-anycrawl-signature'])) {
    return res.status(401).send('Invalid signature');
  }

  // Quick acknowledgment
  res.status(200).json({ received: true });

  // Process asynchronously
  queue.add('process-webhook', req.body);
});

2. Assurer l’idempotence

Utilisez X-Webhook-Delivery-Id pour éviter les traitements en double :

const processedDeliveries = new Set();

app.post('/webhook', (req, res) => {
  const deliveryId = req.headers['x-webhook-delivery-id'];

  if (processedDeliveries.has(deliveryId)) {
    return res.status(200).json({ received: true, duplicate: true });
  }

  processedDeliveries.add(deliveryId);

  // Process event...

  res.status(200).json({ received: true });
});

3. Renvoyer des codes HTTP appropriés

Code HTTPDescriptionComportement AnyCrawl
200-299SuccèsPas de nouvelle tentative
400-499Erreur clientPas de nouvelle tentative (enregistré comme échec)
500-599Erreur serveurNouvelle tentative avec backoff
Délai dépasséTimeout réseauNouvelle tentative avec backoff

4. Journaliser l’activité des webhooks

app.post('/webhook', (req, res) => {
  const deliveryId = req.headers['x-webhook-delivery-id'];
  const eventType = req.headers['x-webhook-event'];

  logger.info('Webhook received', {
    deliveryId,
    eventType,
    timestamp: req.headers['x-webhook-timestamp']
  });

  try {
    processWebhook(req.body, eventType);
    logger.info('Webhook processed', { deliveryId });
    res.status(200).json({ received: true });
  } catch (error) {
    logger.error('Webhook failed', {
      deliveryId,
      error: error.message
    });
    res.status(500).json({ error: 'Processing failed' });
  }
});

5. Liste de contrôle sécurité

  • ✅ Toujours vérifier les signatures
  • ✅ Utiliser HTTPS en production
  • ✅ Ne pas exposer les secrets dans les URL
  • ✅ Mettre en place une limitation de débit
  • ✅ Surveiller les anomalies
  • ✅ Valider la structure des charges utiles

Cas d’usage courants

Notifications Slack

Envoyez les résultats de scraping vers Slack :

app.post('/webhooks/anycrawl', async (req, res) => {
  const { job_id, status, url } = req.body;

  await fetch(process.env.SLACK_WEBHOOK_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      text: `Job ${status}: ${url}\nJob ID: ${job_id}`
    })
  });

  res.status(200).json({ received: true });
});

Alertes e-mail

Envoyez des alertes e-mail en cas d’échec :

app.post('/webhooks/anycrawl', async (req, res) => {
  const eventType = req.headers['x-webhook-event'];

  if (eventType.endsWith('.failed')) {
    await sendEmail({
      to: 'admin@example.com',
      subject: 'AnyCrawl Job Failed',
      body: JSON.stringify(req.body, null, 2)
    });
  }

  res.status(200).json({ received: true });
});

Journalisation en base

Enregistrez les événements webhook en base :

app.post('/webhooks/anycrawl', async (req, res) => {
  const eventType = req.headers['x-webhook-event'];
  const deliveryId = req.headers['x-webhook-delivery-id'];

  await db.webhookEvents.create({
    deliveryId,
    eventType,
    payload: req.body,
    receivedAt: new Date()
  });

  res.status(200).json({ received: true });
});

Dépannage

Aucun événement reçu

Vérifiez :

  • Le webhook est-il actif ? (is_active: true)
  • Les types d’événements sont-ils correctement configurés ?
  • L’URL du webhook est-elle accessible depuis Internet ?
  • Est-elle bloquée par la protection des IP privées ?
  • La portée (all vs specific) est-elle adaptée ?

Échec de la vérification de signature

Problèmes fréquents :

  • Secret incorrect (vérifiez la réponse à la création du webhook)
  • Charge utile non sérialisée en JSON de la même façon avant le hachage
  • Espaces ou format JSON incohérents
  • Mauvais algorithme HMAC (doit être SHA-256)

Taux d’échec élevé

Pistes :

  • Votre point de terminaison répond-il sous 5 secondes ?
  • Renvoyez-vous les bons codes HTTP ?
  • Consultez les messages d’erreur dans l’historique des livraisons
  • Testez en local avec ngrok ou un outil équivalent

Webhook désactivé automatiquement

Cause : 10 échecs consécutifs

Solution :

  1. Corrigez la cause (point de terminaison, vérification de signature, etc.)
  2. Testez avec le point de terminaison de test
  3. Réactivez le webhook :
curl -X PUT "https://api.anycrawl.dev/v1/webhooks/:webhookId/activate" \
  -H "Authorization: Bearer <your-api-key>"

Outils de débogage

Outils de test

Développement local

Exposez votre serveur local avec ngrok :

ngrok http 3000

Utilisez ensuite l’URL ngrok comme URL de webhook :

https://abc123.ngrok.io/webhooks/anycrawl

Limites

ÉlémentLimite
Longueur du nom du webhook1–255 caractères
URL du webhookHTTPS recommandé (production)
Délai d’attente1–60 secondes
Nouvelles tentatives max0–10
Taille de la charge utile1 Mo max
En-têtes personnalisés20 max
Types d’événements par webhookAucune limite

Documentation associée

Table des matières

IntroductionFonctionnalités principalesPoints de terminaison APIÉvénements pris en chargeÉvénements de jobsÉvénements de tâches planifiéesÉvénements SearchÉvénements MapÉvénements de testDémarrage rapideCréer un webhookRéponseParamètres de requêteConfiguration du webhookConfiguration de livraisonMétadonnéesFormat de charge utile webhookEn-têtes HTTPExemples de charge utilescrape.completedscrape.failedtask.executedVérification des signaturesPourquoi vérifier les signatures ?Algorithme de vérificationExemples d’implémentationNode.js / ExpressPython / FlaskGoGérer les webhooksLister tous les webhooksRéponseMettre à jour un webhookTester les webhooksDésactiver / activer un webhookSupprimer un webhookRelancer une livraison échouéeHistorique des livraisonsConsulter les livraisonsParamètres de requêteRéponseMécanisme de nouvelle tentativeQuand les nouvelles tentatives ont lieuCalendrier des nouvelles tentativesDésactivation automatiqueFiltrage de portéeTous les événements (scope: "all")Tâches spécifiques (scope: "specific")Protection des IP privéesComportement par défautAutoriser les webhooks locaux (tests uniquement)Bonnes pratiques1. Répondre rapidement2. Assurer l’idempotence3. Renvoyer des codes HTTP appropriés4. Journaliser l’activité des webhooks5. Liste de contrôle sécuritéCas d’usage courantsNotifications SlackAlertes e-mailJournalisation en baseDépannageAucun événement reçuÉchec de la vérification de signatureTaux d’échec élevéWebhook désactivé automatiquementOutils de débogageOutils de testDéveloppement localLimitesDocumentation associée