Aller au contenu

Webhooks

Au lieu d’interroger, vous pouvez demander à Accessful d’envoyer par POST un événement signé à votre serveur dès qu’une tâche change d’état.

Il n’y a ni tableau de bord ni endpoint d’enregistrement. Vous attachez un callback à chaque téléversement, et vous choisissez vous-même le secret de signature — Accessful n’en émet aucun.

  • Téléversement multipart — envoyez les champs de formulaire webhookUrl et secret.
  • Téléversement par URL — envoyez les champs JSON callbackUrl et hmacSignature.
Terminal window
curl -X POST "https://api.accessful.de/api/v1/upload-service/pdf/upload" \
-H "X-API-Key: $ACCESSFUL_API_KEY" \
-F "files=@document.pdf" \
-F "webhookUrl=https://votre-app.example.com/hooks/accessful" \
-F "secret=$VOTRE_WEBHOOK_SECRET"
type d’événement Se déclenche quand
case.queued Le PDF a été accepté et est entré dans la file d’analyse. Se déclenche une seule fois et contient la position dans la file de départ.
case.running La tâche a commencé son traitement. Peut se déclencher plusieurs fois (une par itération).
case.completed Le résultat PDF/UA est prêt à être téléchargé.
case.failed Le traitement a échoué.
case.canceled Le cas a été annulé.
case.quota_exceeded Rejeté car le quota contractuel est épuisé.

L’état quota_pending n’est pas livré en tant que webhook.

Le corps de la requête est au format JSON. Content-Type: application/json.

{
"id": "f1d2c3b4-0000-4a1e-8f3c-2d6b5a9e1c40",
"type": "case.completed",
"apiVersion": "2026-06-26",
"occurredAt": "2026-06-26T12:34:56Z",
"data": {
"caseId": "7c2f1e4a-9b0d-4a1e-8f3c-2d6b5a9e1c40",
"fileName": "document.pdf",
"jobStatus": "completed"
}
}
Champ Type Notes
id UUID ID de l’événement. Utilisez-le comme clé d’idempotence (voir ci-dessous).
type string L’un des types d’événement ci-dessus.
apiVersion string Version du contrat (2026-06-26). Épinglez-la pour détecter les changements.
occurredAt ISO-8601 Date de création de l’événement ; stable d’une tentative à l’autre.
data.caseId UUID Le cas concerné par cet événement.
data.fileName string Le nom du fichier.
data.jobStatus string Statut brut de la tâche, p. ex. queued, completed, failed, canceled, quota_exceeded.
data.queuePosition integer case.queued uniquement — rang de départ dans la file, à partir de 1.
data.queueTotal integer case.queued uniquement — nombre total de cas en attente à ce moment-là.
data.estimatedWaitSeconds integer case.queued uniquement — estimation indicative du temps d’attente en secondes.

Les champs nuls sont omis du JSON.

L’événement case.queued est le pendant push de la position dans la file du statut de tâche : il se déclenche une seule fois, au moment où un PDF est accepté, et contient la position de départ afin que vous puissiez afficher « vous êtes 7e dans la file, ~5 min » sans interrogation.

{
"id": "a3b1c2d4-0000-4a1e-8f3c-2d6b5a9e1c40",
"type": "case.queued",
"apiVersion": "2026-06-26",
"occurredAt": "2026-06-26T12:30:00Z",
"data": {
"caseId": "7c2f1e4a-9b0d-4a1e-8f3c-2d6b5a9e1c40",
"fileName": "document.pdf",
"jobStatus": "queued",
"queuePosition": 7,
"queueTotal": 23,
"estimatedWaitSeconds": 300
}
}

Il s’agit d’un événement de cycle de vie discret, pas d’un flux en temps réel — la position est un instantané au moment de l’acceptation et n’est pas renvoyée à mesure que la file avance. Pour une position continuellement mise à jour, interrogez GET /job-status/{caseId}, dont la réponse porte les mêmes champs queue* tant que le cas est en attente. Les champs queue* n’apparaissent que sur case.queued ; tout autre événement les omet.

En-tête Exemple Objet
X-Accessful-Signature t=1749126896,v1=9f86d0… Signature HMAC — à vérifier
X-Accessful-Webhook-Timestamp 1749126896 Secondes Unix ; identique au t= ci-dessus
X-Accessful-Event-Id f1d2c3b4-… Égale id ; clé d’idempotence
X-Accessful-Event-Type case.completed Routage sans analyser le corps
X-Accessful-Case-Id 7c2f1e4a-… L’ID du cas
X-Accessful-Delivery-Attempt 1 Compteur de tentatives, à partir de 1
X-Signature n4bQgY… legacy HMAC base64 sur le corps uniquement

L’en-tête X-Accessful-Signature a la forme t=<unix>,v1=<hex>. Recalculez-la et comparez en temps constant :

  1. Lisez t et v1 dans l’en-tête.
  2. Calculez HMAC-SHA256(secret, "<t>.<corps brut>") et encodez-le en hexadécimal. La chaîne signée est l’horodatage, un point littéral, puis le corps brut exact de la requête — vérifiez avant l’analyse JSON.
  3. Comparez en temps constant avec v1.
  4. Éventuellement, rejetez si t date de plus de quelques minutes (protection contre le rejeu).
import crypto from 'node:crypto';
// `rawBody` doit correspondre aux octets exacts reçus (p. ex. express.raw()).
function verify(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(signatureHeader.split(',').map((p) => p.split('=')));
const expected = crypto
.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
const valid = crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300; // 5 min
return valid && fresh;
}
  • Succès = toute réponse 2xx dans le délai imparti (5 s pour se connecter, 10 s pour répondre). Répondez vite et effectuez votre travail de manière asynchrone.
  • Échec (non-2xx, délai dépassé ou erreur de connexion) est réessayé jusqu’à 10 tentatives avec un backoff exponentiel et du jitter — environ 10s → 30s → 1,5m → 4,5m → 13,5m → 40m → 2h, puis plafonné à 6h, moins jusqu’à 20 % de jitter. La fenêtre complète s’étend sur plusieurs heures.
  • Après 10 tentatives échouées, l’événement est abandonné. Contactez le support pour une nouvelle livraison.
  • Idempotence : le même événement peut arriver plus d’une fois (p. ex. une nouvelle tentative après que votre endpoint a déjà réussi). Dédupliquez sur X-Accessful-Event-Id — il est stable à chaque tentative.