Géocodage par lot avec webhooks et clés d’idempotence

Un envoi par lot est l’appel que vous voulez le moins lancer deux fois : un délai réseau relancé à l’aveugle peut soumettre, et facturer, le fichier une seconde fois. Deux mécanismes le rendent sûr. Une Idempotency-Key rend l’envoi répétable, et un webhook vous prévient quand le job se termine, sans interroger en boucle.

L’URL de base https://api.addrova.eu n’est pas encore en ligne. Tout ce qui suit suit la spécification de l’API. [À VÉRIFIER : service ouvert, URL de base définitive.]

1. Rendre l’envoi répétable

Envoyez un en-tête Idempotency-Key avec le fichier. Pendant 24 heures, par tenant, la même clé avec la même requête renvoie le résultat d’origine (Idempotent-Replayed: true) et rien n’est facturé deux fois. La même clé avec une requête différente est refusée avec 422 idempotency_key_reused ; une requête encore en traitement répond 409 idempotency_in_progress.

printf 'adresse\n4 place d'\''Armes Belfort\n' > adresses.csv
KEY=$(date +%s)-envoi-1
curl -s https://api.addrova.eu/v1/batch \
  -H "X-API-Key: $ADDROVA_API_KEY" \
  -H "Idempotency-Key: $KEY" \
  -F file=@adresses.csv -F address_column=adresse

Relancez la même commande avec la même KEY et vous récupérez le même job. Générez la clé une fois par envoi logique et gardez-la avec votre état de relance.

Le service ne conserve qu’une empreinte SHA-256 de la requête et, pour un lot, l’identifiant du job, jamais l’adresse ni la réponse (ZL-28).

2. Demander un webhook

Ajoutez webhook_url à l’envoi : un point d’accès HTTPS appelé une fois, à la fin du job, qu’il ait réussi ou échoué. La réponse 202 renvoie une seule fois un secret de signature dans webhook_secret (whsec_…) ; conservez-le, il n’est plus affiché.

curl https://api.addrova.eu/v1/batch \
  -H "X-API-Key: $ADDROVA_API_KEY" \
  -F file=@adresses.csv -F address_column=adresse \
  -F webhook_url=https://example.com/hooks/addrova

La charge utile contient l’identifiant du job, le statut, les compteurs et les liens, jamais une ligne du fichier ni une adresse, et l’URL et le secret du webhook ne sont jamais journalisés (ZL-27). Les livraisons échouées sont relancées jusqu’à 5 fois (tout de suite, puis après 10 s, 1 min, 5 min et 30 min) jusqu’à ce que votre point d’accès réponde 2xx.

3. Vérifier la signature

Le webhook suit le format Standard Webhooks. Trois en-têtes comptent : webhook-id, webhook-timestamp et webhook-signature. La signature est v1, suivi du HMAC-SHA256 en base64 de {webhook-id}.{webhook-timestamp}.{corps}, avec le secret décodé comme clé. Rejetez les horodatages de plus de 5 minutes et dédoublonnez sur webhook-id. Cette vérification tourne seule, avec un secret inventé :

import base64
import hashlib
import hmac
import time

def sign(secret: str, msg_id: str, timestamp: int, body: bytes) -> str:
    key = base64.b64decode(secret.removeprefix("whsec_"))
    mac = hmac.new(key, f"{msg_id}.{timestamp}.".encode() + body, hashlib.sha256).digest()
    return "v1," + base64.b64encode(mac).decode()

def verify(secret: str, headers: dict, body: bytes, tolerance: int = 300) -> bool:
    if abs(time.time() - int(headers["webhook-timestamp"])) > tolerance:
        return False
    expected = sign(secret, headers["webhook-id"], int(headers["webhook-timestamp"]), body)
    return any(hmac.compare_digest(expected, part) for part in headers["webhook-signature"].split(" "))

secret = "whsec_" + base64.b64encode(b"a-made-up-signing-key-32-bytes!!").decode()
body = b'{"type":"batch.finished","id":"job_1"}'
now = int(time.time())
headers = {"webhook-id": "msg_1", "webhook-timestamp": str(now), "webhook-signature": sign(secret, "msg_1", now, body)}

assert verify(secret, headers, body)
assert not verify(secret, headers, body + b" ")  # un corps modifié échoue
assert not verify(secret, {**headers, "webhook-timestamp": str(now - 3600)}, body)  # trop ancien
print("vérifications de signature OK")

Les SDK officiels fournissent un utilitaire verify_webhook, vous l’écrivez donc rarement vous-même : voir la page SDK.

Liste de contrôle

  • Générez une Idempotency-Key par envoi logique, réutilisez-la à chaque relance.
  • Stockez le webhook_secret à sa réception.
  • Vérifiez la signature avant de faire confiance à la charge utile ; répondez 2xx vite et travaillez ensuite.
  • Dédoublonnez sur webhook-id : une relance peut livrer deux fois le même événement.
  • N’interrogez le job qu’en secours : voir la référence du lot.