Account e integrazioni

Ricevere e verificare un webhook firmato

Quando viene registrata una risposta, Kiosform prepara una richiesta POST JSON al tuo server. Le risposte sincronizzate da un chiosco usano lo stesso meccanismo. I titoli della sezione e il comando di test sono in italiano, ma alcuni campi e il pulsante di aggiunta restano in francese: qui li trovi con una spiegazione in italiano.

Aggiornato il

Prima di iniziare

  • Assicurati di avere un endpoint HTTPS accessibile pubblicamente sulla porta 443.
  • Prepara un archivio persistente per la deduplicazione e conserva il segreto solo sul server.

Passaggi

  1. Aggiungi il destinatario

    Nelle risposte del modulo, trova “Invii automatici ai tuoi strumenti (webhook) e cronologia”. Compila “URL de destination (HTTPS)” (URL di destinazione, HTTPS) e una descrizione, poi usa “Ajouter le webhook signé” (aggiungi il webhook firmato). Il tuo endpoint appare con il relativo segreto.

  2. Controlla la consegna

    Usa “Invia un test”. La cronologia distingue le consegne in attesa, i tentativi e i risultati. Lo stato “delivered” (consegnato) significa che il destinatario ha restituito uno stato HTTP 2xx. Controlla anche i tuoi log.

  3. Verifica la firma

    Leggi il corpo HTTP grezzo prima di decodificare il JSON. Controlla il timestamp e verifica l'HMAC di timestamp.raw_body usando il segreto del webhook. Questa notazione indica il timestamp seguito da un punto letterale e dal corpo grezzo non modificato. Usa uno degli esempi qui sotto.

  4. Registra ogni consegna senza duplicati

    Usa X-Kiosform-Delivery-Id come chiave univoca persistente. Registra questa chiave e l'operazione applicativa nella stessa transazione. La ricezione dello stesso identificatore non deve ripetere l'operazione.

Schermata Kiosform: Ricevere e verificare un webhook firmato, con dati di esempio.
Schermata dell'app web con italiano selezionato e dati dimostrativi. Alcune etichette sono ancora in inglese e in francese. Apri l'immagine per ingrandirla.

Corpo e intestazioni

L'evento response.created contiene direttamente form_id, form_version_id, workspace_id, response_id, source, device_id, client_created_at, submitted_at, deferred, idempotency_key e answers. Queste proprietà si trovano alla radice del JSON, non in un oggetto response. L'evento webhook.test contiene event, form_id e created_at; non è una risposta di un partecipante.

X-Kiosform-Signature ha la forma t=unix_timestamp,v1=hex_signature, dove unix_timestamp è il timestamp Unix e hex_signature è la firma in esadecimale. X-Kiosform-Delivery-Id resta invariato per una consegna, anche nei tentativi successivi. Il corpo resta invariato; il timestamp e la firma vengono rinnovati.

answers contiene i valori raccolti e può includere dati personali come un nome o un indirizzo email. Scegli un server destinatario autorizzato, proteggine l'archiviazione ed evita di registrare il corpo completo nei log. Conserva i segreti e i contenuti ricevuti sul server, mai in uno script pubblico eseguito nel browser.

{
  "event": "response.created",
  "form_id": "00000000-0000-4000-8000-000000000001",
  "form_version_id": "00000000-0000-4000-8000-000000000002",
  "workspace_id": "00000000-0000-4000-8000-000000000003",
  "response_id": "00000000-0000-4000-8000-000000000004",
  "source": "web",
  "device_id": null,
  "client_created_at": "2026-10-18T08:00:00.000Z",
  "submitted_at": "2026-10-18T08:00:01.000Z",
  "deferred": false,
  "idempotency_key": "00000000-0000-4000-8000-000000000005",
  "answers": {
    "prenom": "Camille"
  }
}

Esempi di verifica

Queste funzioni richiedono i byte del corpo, il segreto e l'intestazione esatta. Tollerano uno scarto dell'orologio di cinque minuti in entrambe le direzioni: mantieni quindi preciso l'orologio del server. Rifiuta la richiesta se la funzione restituisce false. Analizza il JSON e applica la deduplicazione solo dopo la verifica.

Node.js

import { createHmac, timingSafeEqual } from 'node:crypto';

// rawBody est un Buffer : ne pas reconstituer le JSON après décodage.
export function verifyKiosformWebhook(secret, rawBody, header, now = Math.floor(Date.now() / 1000)) {
  if (!secret || !Buffer.isBuffer(rawBody) || typeof header !== 'string') return false;
  const parts = /^t=([1-9][0-9]{0,11}),v1=([a-f0-9]{64})$/.exec(header);
  if (!parts || parts[0] !== header || !Number.isFinite(now)) return false;
  const timestamp = Number(parts[1]);
  if (Math.abs(now - timestamp) > 300) return false;
  const expected = createHmac('sha256', secret)
    .update(parts[1] + '.', 'utf8').update(rawBody).digest();
  const received = Buffer.from(parts[2], 'hex');
  return timingSafeEqual(expected, received);
}
Scarica l'esempio Node.js

Python 3

import hashlib
import hmac
import re
import time


def verify_kiosform_webhook(secret, raw_body, header, now=None):
    """raw_body contient les octets HTTP, avant le décodage JSON."""
    if not secret or not isinstance(raw_body, bytes) or not isinstance(header, str):
        return False
    parts = re.fullmatch(r"t=([1-9][0-9]{0,11}),v1=([a-f0-9]{64})", header)
    if parts is None:
        return False
    timestamp = int(parts[1])
    if abs((time.time() if now is None else now) - timestamp) > 300:
        return False
    signed = (parts[1] + ".").encode("ascii") + raw_body
    expected = hmac.new(secret.encode("utf-8"), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts[2])
Scarica l'esempio Python 3

PHP

<?php
// $rawBody = file_get_contents('php://input'), avant json_decode.
function verifyKiosformWebhook(string $secret, string $rawBody, string $header, ?int $now = null): bool
{
    if ($secret === '' || preg_match('/\At=([1-9][0-9]{0,11}),v1=([a-f0-9]{64})\z/', $header, $parts) !== 1) {
        return false;
    }
    if (abs(($now ?? time()) - (int) $parts[1]) > 300) {
        return false;
    }
    $expected = hash_hmac('sha256', $parts[1] . '.' . $rawBody, $secret);
    return hash_equals($expected, $parts[2]);
}
Scarica l'esempio PHP

Tentativi successivi ed errori

Sono possibili fino a quattro tentativi: la consegna iniziale, poi tre tentativi successivi eseguibili dopo 1, 10 e 60 secondi. Il worker di produzione viene eseguito ogni dieci secondi, quindi questi intervalli non garantiscono una consegna al secondo esatto.

Un errore di rete o uno stato HTTP pari o superiore a 300 conta come un errore di consegna. I reindirizzamenti non vengono seguiti. Le destinazioni locali o private vengono rifiutate. Dopo venti errori consecutivi, l'endpoint viene disabilitato automaticamente. Una risposta 2xx riuscita azzera il contatore.

La consegna avviene almeno una volta, non esattamente una volta. Un arresto anomalo dopo la ricezione può causare un'altra consegna. Rispondi rapidamente dopo aver registrato il lavoro in modo persistente, poi gestisci le operazioni lunghe nella tua coda.

In caso di difficoltà

Una consegna è ancora in attesa

Controlla il certificato HTTPS, la porta 443, l'accessibilità pubblica e la cronologia dei tentativi. Confronta l'identificatore di consegna con quello ricevuto dal tuo server.

La firma viene rifiutata

Controlla di usare il segreto corretto, che l'orologio sia preciso e che tu stia leggendo il corpo grezzo. Riconvertire in testo un JSON già analizzato può modificare gli spazi o l'ordine delle chiavi e invalidare l'HMAC.