Webhooks
Envoyez depuis Potato des notifications HTTP par webhook signées en HMAC pour 7 types d'événements d'annotation, avec relance à backoff exponentiel, surveillance côté administration et vérification en Python ou en Node.
Nouveau dans la v2.4.0
Les webhooks permettent à Potato de prévenir des systèmes externes quand un événement d'annotation survient, sans interrogation répétée. Reliez-les à des pipelines de données, déclenchez des alertes, mettez à jour des tableaux de bord ou lancez automatiquement des traitements en aval.
Vue d'ensemble
Potato envoie une requête HTTP POST au point de terminaison que vous avez configuré chaque fois qu'un événement pris en charge se déclenche. Les payloads sont en JSON et signés en HMAC-SHA256, ce qui vous permet de vérifier qu'ils viennent bien de votre instance Potato.
La livraison des webhooks est entièrement non bloquante : les requêtes d'annotation ne sont jamais retardées pendant qu'un webhook est en cours. Les livraisons en échec sont relancées avec un backoff exponentiel.
Configuration
Ajoutez une section webhooks à votre configuration YAML :
webhooks:
enabled: true
endpoints:
- name: "my-pipeline"
url: "https://your-system.example.com/potato-events"
secret: "your-signing-secret" # optional but recommended
events:
- annotation.created
- item.fully_annotated
- task.completed
active: true
timeout: 10Plusieurs points de terminaison
Vous pouvez configurer plusieurs points de terminaison, chacun avec ses propres abonnements aux événements :
webhooks:
enabled: true
endpoints:
- name: "data-pipeline"
url: "https://pipeline.example.com/annotations"
secret: ${WEBHOOK_SECRET_1}
events:
- annotation.created
- item.fully_annotated
- name: "slack-alerts"
url: "https://hooks.slack.com/services/..."
events:
- task.completed
- quality.attention_check_failed
- name: "catch-all"
url: "https://logging.example.com/potato"
events:
- "*" # subscribe to all eventsTypes d'événements
| Événement | Se déclenche quand |
|---|---|
annotation.created | Un annotateur soumet une étiquette pour une instance |
annotation.updated | Un annotateur modifie une étiquette déjà soumise |
item.fully_annotated | Une instance atteint le recoupement d'annotations exigé |
task.completed | Toutes les instances de la tâche sont entièrement annotées |
user.phase_completed | Un annotateur termine une phase (flux du mode solo) |
quality.attention_check_failed | Un annotateur échoue à un contrôle d'attention |
webhook.test | Déclenché manuellement via l'API d'administration, pour tester |
Utilisez "*" pour vous abonner à tous les types d'événements, actuels et futurs.
Format du payload
Tous les événements partagent une même enveloppe :
{
"event_id": "evt_01HXYZ...",
"event_type": "annotation.created",
"timestamp": "2026-03-17T14:23:01Z",
"task_name": "sentiment-study",
"data": {
...
}
}Payload de annotation.created
{
"event_type": "annotation.created",
"data": {
"annotator_id": "user123",
"instance_id": "doc_042",
"annotation": {
"sentiment": "positive",
"confidence": "high"
},
"submitted_at": "2026-03-17T14:23:01Z"
}
}Payload de item.fully_annotated
{
"event_type": "item.fully_annotated",
"data": {
"instance_id": "doc_042",
"annotator_count": 3,
"annotations": [
{"annotator_id": "user1", "sentiment": "positive"},
{"annotator_id": "user2", "sentiment": "positive"},
{"annotator_id": "user3", "sentiment": "neutral"}
]
}
}Payload de task.completed
{
"event_type": "task.completed",
"data": {
"task_name": "sentiment-study",
"total_instances": 500,
"total_annotations": 1500,
"completed_at": "2026-03-17T15:00:00Z"
}
}Vérifier les signatures
Quand un secret est configuré, Potato signe chaque requête selon Standard Webhooks (HMAC-SHA256). Trois en-têtes sont ajoutés :
| En-tête | Valeur |
|---|---|
webhook-id | Identifiant unique de livraison |
webhook-timestamp | Horodatage Unix de la livraison |
webhook-signature | Signature HMAC-SHA256 |
Vérification en Python
import hmac
import hashlib
import time
def verify_webhook(payload_bytes: bytes, headers: dict, secret: str) -> bool:
webhook_id = headers.get("webhook-id", "")
timestamp = headers.get("webhook-timestamp", "")
signature = headers.get("webhook-signature", "")
# Reject stale requests (older than 5 minutes)
if abs(time.time() - int(timestamp)) > 300:
return False
signed_content = f"{webhook_id}.{timestamp}.{payload_bytes.decode()}"
expected = hmac.new(
secret.encode(),
signed_content.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"v1,{expected}", signature)Vérification en Node.js
const crypto = require('crypto');
function verifyWebhook(payload, headers, secret) {
const webhookId = headers['webhook-id'];
const timestamp = headers['webhook-timestamp'];
const signature = headers['webhook-signature'];
// Reject stale requests
if (Math.abs(Date.now() / 1000 - parseInt(timestamp)) > 300) {
return false;
}
const signedContent = `${webhookId}.${timestamp}.${payload}`;
const expected = crypto
.createHmac('sha256', secret)
.update(signedContent)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(`v1,${expected}`),
Buffer.from(signature)
);
}Comportement des relances
Les livraisons en échec (réponse non 2xx ou dépassement de délai) sont relancées automatiquement :
| Tentative | Délai |
|---|---|
| 1 (initiale) | Immédiat |
| 2 | 5 secondes |
| 3 | 30 secondes |
| 4 | 5 minutes |
| 5 | 30 minutes |
| 6 | 1 heure |
Après 6 tentatives infructueuses, la livraison est marquée comme définitivement en échec. La file de relance est conservée en SQLite dans {output_dir}/.webhooks/webhook_retries.db : les relances en attente survivent aux redémarrages du serveur.
Surveillance et tests
API d'administration
Consultez l'état des webhooks et leurs statistiques :
# List all webhooks and their delivery statistics
curl -H "X-API-Key: $ADMIN_API_KEY" \
http://localhost:8000/admin/api/webhooksRéponse :
{
"endpoints": [
{
"name": "my-pipeline",
"url": "https://...",
"events": ["annotation.created"],
"active": true,
"stats": {
"total_emitted": 1240,
"total_failed": 3,
"pending_retries": 0,
"last_success": "2026-03-17T14:23:01Z"
}
}
]
}Envoyer un webhook de test
curl -X POST -H "X-API-Key: $ADMIN_API_KEY" \
http://localhost:8000/admin/api/webhooks/test \
-H "Content-Type: application/json" \
-d '{"endpoint_name": "my-pipeline"}'Un événement webhook.test part immédiatement vers le point de terminaison nommé.
Référence complète de configuration
webhooks:
enabled: true
endpoints:
- name: string # unique name for this endpoint
url: string # HTTPS URL to POST to
secret: string # optional HMAC secret for signature verification
events: # list of event types, or ["*"] for all
- annotation.created
active: true # set false to disable without removing
timeout: 10 # request timeout in seconds (default: 10)
max_retries: 6 # max retry attempts (default: 6)Pour aller plus loin
- Tableau de bord d'administration — suivre l'avancement des annotations et gérer les utilisateurs
- Contrôle de la qualité — configurer les contrôles d'attention qui déclenchent
quality.attention_check_failed - Mode solo — flux par phases qui déclenche
user.phase_completed - Formats d'exportation — autres façons de sortir les annotations de Potato
Pour les détails d'implémentation, consultez la documentation source.