Webhooks
I webhook permettono di ricevere notifiche automatiche sull'avanzamento delle pratiche di firma. Attraverso i webhook, il vostro sistema viene informato in tempo reale quando una pratica viene firmata, rifiutata, scade o cambia stato, senza dover eseguire polling continuo.
Come Funzionano
Quando un evento significativo si verifica su una pratica (firma, rifiuto, scadenza, ecc.), Askme Sign invia una richiesta HTTP POST all'URL da voi configurato. Il corpo della richiesta contiene un payload JSON con le informazioni sull'evento.
[Firmatario firma] → [Askme Sign aggiorna stato] → [POST al vostro endpoint]
Configurazione del Webhook
I webhook si configurano al momento della creazione della pratica, specificando il campo webhook (singolo endpoint) o webhooks (più endpoint) nel body della richiesta POST /api/v2/files/send.
Campi di configurazione
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
url | string | Sì | URL del vostro endpoint. Supporta placeholder: {{idFile}}, {{tenantCode}}, {{signerEmail}}, {{status}}, {{action}}. |
authMode | string | No | Modalità di autenticazione: basic, header, oauth2. |
authConfig | string | No | Chiave di configurazione per le credenziali (per tenant). |
username | string | No | Username per Basic Auth (se authMode = basic). |
password | string | No | Password per Basic Auth (se authMode = basic). |
Esempio: webhook nella creazione pratica
{
"documents": [{ "filename": "contratto.pdf", "base64content": "..." }],
"name": "Contratto con webhook",
"sendNotifications": true,
"webhook": {
"url": "https://your-server.com/webhooks/askmesign",
"authMode": "basic",
"username": "webhook_user",
"password": "webhook_secret"
},
"signers": [
{
"email": "[email protected]",
"firstName": "Mario",
"lastName": "Rossi",
"action": "S",
"signatureCoordinates": [
{ "document": 0, "page": 1, "signatureType": "S", "positionX": 0.6, "positionY": 0.8, "width": 0.2, "height": 0.08 }
]
}
]
}
Webhook multipli
Per notificare più endpoint, usare il campo webhooks (array):
{
"webhooks": [
{
"url": "https://server-a.com/webhook",
"authMode": "basic",
"username": "user_a",
"password": "pass_a"
},
{
"url": "https://server-b.com/webhook?idFile={{idFile}}&status={{status}}",
"authMode": "header"
}
]
}
Webhook per firmatario
Ogni firmatario può avere un webhook dedicato, utile per notificare sistemi diversi in base al firmatario:
{
"signers": [
{
"email": "[email protected]",
"action": "S",
"webhook": {
"url": "https://crm.example.com/webhook/firma-cliente",
"authMode": "basic",
"username": "crm_user",
"password": "crm_pass"
}
}
]
}
Payload della Notifica
Struttura JSON
Quando un evento si verifica, il sistema invia il seguente payload al vostro endpoint:
{
"idFile": 12345,
"tenantCode": "TENANT01",
"name": "Contratto Cliente Rossi",
"status": "F",
"action": "S",
"signerEmail": "[email protected]",
"notes": null,
"protocol": "PROT-2024-001",
"externalId": "CRM-98765",
"time": "2024-10-23T10:09:00.000+0200"
}
Descrizione dei campi
| Campo | Tipo | Descrizione |
|---|---|---|
idFile | number | ID univoco della pratica in Askme Sign. |
tenantCode | string | Codice del tenant a cui appartiene la pratica. |
name | string | Nome della pratica. |
status | char | Stato corrente della pratica dopo l'evento. |
action | char | Tipo di azione che ha generato l'evento. |
signerEmail | string | Email del firmatario che ha eseguito l'azione. |
notes | string | Note associate all'azione (es. motivazione del rifiuto). null per azioni di sottomissione e rimozione. |
protocol | string | Numero di protocollo della pratica (se configurato). |
externalId | string | ID esterno della pratica nel vostro sistema (se specificato in fase di creazione). |
time | string | Data/ora dell'evento in formato ISO 8601 (es. 2024-10-23T10:09:00.000+0200). |
Eventi e Codici
Codici di stato (status)
| Codice | Descrizione | Quando si verifica |
|---|---|---|
L | In lavorazione | La pratica è in corso: un firmatario ha firmato ma ce ne sono altri in attesa. |
F | Firmato | Tutti i firmatari hanno completato le azioni richieste. La pratica è chiusa con successo. |
R | Rifiutato | Un firmatario ha rifiutato la pratica. |
A | Completato | La pratica è stata approvata (in workflow con approvazione). |
E | Scaduto | La data di scadenza è stata superata senza completamento. |
V | Annullato | La pratica è stata annullata. |
Codici di azione (action)
| Codice | Descrizione |
|---|---|
I | Sottomissione (pratica creata e inviata) |
S | Firma elettronica semplice |
E | FES (Firma Elettronica Semplice conforme eIDAS) |
T | FES con OTP |
V | FEA (Firma Elettronica Avanzata) |
Q | FEQ (Firma Elettronica Qualificata) |
O | FEQ One-Shot |
J | Firma IO |
P | Visto |
W | Approvazione |
D | Rifiuto |
N | Annullamento |
Scenari comuni
| Scenario | status | action | Descrizione |
|---|---|---|---|
| Pratica creata | L | I | Il processo di firma è stato avviato. |
| Firma intermedia | L | S/E/T/V/Q | Un firmatario ha firmato, ma altri devono ancora firmare. |
| Ultima firma | F | S/E/T/V/Q | L'ultimo firmatario ha firmato. Pratica completata. |
| Rifiuto | R | D | Un firmatario ha rifiutato. Il campo notes può contenere la motivazione. |
| Scadenza | E | — | La pratica è scaduta automaticamente. |
| Annullamento | V | N | La pratica è stata annullata manualmente. |
Autenticazione del Webhook
Basic Auth
POST https://your-server.com/webhooks/askmesign
Authorization: Basic base64(username:password)
Content-Type: application/json
Header personalizzato
Con authMode: "header", il sistema invia un header di autenticazione configurato a livello di tenant (nome e valore dell'header sono definiti nella configurazione server).
OAuth 2.0
Con authMode: "oauth2", il sistema ottiene un Bearer token dalla configurazione del tenant e lo include nell'header Authorization.
Retry e Affidabilità
Il sistema gestisce automaticamente i retry in caso di fallimento della chiamata:
- Se il vostro endpoint non risponde o restituisce un errore (status >= 400), il sistema ripianifica l'invio
- Ogni esecuzione registra: numero di tentativi, codice di risposta, timestamp dell'ultimo tentativo e del prossimo pianificato
- I retry continuano fino al raggiungimento del limite configurato
Il vostro endpoint deve rispondere con HTTP 200 entro pochi secondi. Se l'elaborazione richiede tempo, accodare il lavoro in una coda asincrona e rispondere immediatamente.
Personalizzazione del Payload
Il payload può essere personalizzato a livello di tenant tramite un template nella configurazione del sistema. Le variabili disponibili sono:
| Variabile | Descrizione |
|---|---|
$idFile | ID della pratica |
$tenantCode | Codice tenant |
$name | Nome della pratica |
$status | Stato corrente |
$action | Azione eseguita |
$externalId | ID esterno |
$time | Timestamp dell'evento |
La personalizzazione viene configurata tramite la chiave webhook.body.[authConfig].content nelle impostazioni del tenant. Se non configurato, viene usato il payload JSON standard.
Implementazione dell'Endpoint
Checklist
- Endpoint HTTPS accessibile da Internet che accetti richieste POST
- Autenticazione: implementare il meccanismo scelto (Basic Auth, API Key, OAuth)
- Risposta rapida: restituire HTTP 200 entro pochi secondi
- Idempotenza: gestire eventuali notifiche duplicate (stesso evento ricevuto più volte)
- Logging: registrare tutti i webhook ricevuti per debugging e auditing
Esempio: Node.js/Express
const express = require('express');
const app = express();
app.use(express.json());
// Middleware autenticazione Basic
const authenticate = (req, res, next) => {
const auth = req.headers.authorization;
if (!auth || !auth.startsWith('Basic ')) {
return res.status(401).json({ error: 'Unauthorized' });
}
const credentials = Buffer.from(auth.slice(6), 'base64').toString();
const [username, password] = credentials.split(':');
if (username !== process.env.WEBHOOK_USER || password !== process.env.WEBHOOK_PASS) {
return res.status(401).json({ error: 'Invalid credentials' });
}
next();
};
app.post('/webhooks/askmesign', authenticate, (req, res) => {
const { idFile, status, action, signerEmail, time } = req.body;
console.log(`[${time}] Pratica ${idFile}: status=${status}, action=${action}, signer=${signerEmail}`);
// Elaborazione asincrona
processWebhookAsync(req.body).catch(console.error);
// Risposta immediata
res.status(200).json({ success: true });
});
app.listen(3000, () => console.log('Webhook server avviato sulla porta 3000'));
Esempio: Python/Flask
from flask import Flask, request, jsonify
import base64
import os
app = Flask(__name__)
def verify_basic_auth():
auth = request.headers.get("Authorization", "")
if not auth.startswith("Basic "):
return False
credentials = base64.b64decode(auth[6:]).decode()
username, password = credentials.split(":", 1)
return (username == os.environ["WEBHOOK_USER"] and
password == os.environ["WEBHOOK_PASS"])
@app.route("/webhooks/askmesign", methods=["POST"])
def handle_webhook():
if not verify_basic_auth():
return jsonify({"error": "Unauthorized"}), 401
data = request.get_json()
print(f"[{data['time']}] Pratica {data['idFile']}: "
f"status={data['status']}, action={data['action']}, "
f"signer={data['signerEmail']}")
# Elaborazione asincrona consigliata in produzione
return jsonify({"success": True}), 200
if __name__ == "__main__":
app.run(port=3000)
Best Practices
- Preferire i webhook al polling: i webhook sono più efficienti e forniscono aggiornamenti in tempo reale
- Implementare idempotenza: il vostro endpoint potrebbe ricevere lo stesso evento più volte in caso di retry
- Rispondere velocemente: restituire HTTP 200 entro pochi secondi, elaborare in modo asincrono
- Usare HTTPS: proteggere sempre l'endpoint con HTTPS
- Autenticazione: implementare sempre Basic Auth, API Key o OAuth per proteggere l'endpoint
- Logging: registrare ogni webhook per debugging e auditing
- Gestire tutti gli stati: implementare la logica per tutti i codici di stato possibili, non solo
F(firmato) - Monitorare i fallimenti: configurare alerting se il vostro endpoint smette di ricevere webhook