Scheduler
Lo Scheduler permette di programmare chiamate HTTP (callback) differite: "tra N secondi (o a una certa data/ora) chiama questo URL con questo payload". Supporta job one-shot e ricorrenti (a intervallo o a calendario), con retry automatico, dead-letter e una dashboard per ispezionare la coda e lo storico.
Come funziona
Lo stato dei job vive in un container dedicato: lo store è la coda. Un poller interno (ogni ~1 minuto) preleva i job scaduti, ne esegue il callback HTTP e ne aggiorna l'esito. Di conseguenza:
- La precisione di scatto è pari all'intervallo di poll (~1 minuto): non è pensato per callback al secondo.
- Il modello è at-least-once: i ricevitori devono tollerare doppie chiamate (usa l'idempotency key, vedi sotto).
- Tutti gli istanti sono in UTC nello store; la pianificazione di calendario usa l'ora di parete italiana (vedi Ricorrenza).
Endpoint
| Metodo | Endpoint | Descrizione | Permesso |
|---|---|---|---|
POST |
/scheduled-jobs |
Crea un job differito | write |
GET |
/scheduled-jobs |
Elenco paginato (filtri: status, from, to) |
read |
GET |
/scheduled-jobs/{id} |
Dettaglio di un job | read |
DELETE |
/scheduled-jobs/{id} |
Annulla un job (solo se ancora Pending) |
write |
Creare un job
curl -X POST "https://api.contit.cloud/scheduled-jobs" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"name": "Reminder ordine",
"delaySeconds": 3600,
"callbackUrl": "https://example.com/hooks/reminder",
"method": "POST",
"headers": { "X-Source": "contit" },
"payload": { "orderId": "abc123" },
"maxRetries": 5
}'
Risposta: il job creato, con id e status: "Pending".
Campi della richiesta
| Campo | Tipo | Descrizione |
|---|---|---|
name |
string | Nome descrittivo (facoltativo). |
delaySeconds |
number | Secondi di attesa da adesso. Alternativo a fireAtUtc. |
fireAtUtc |
datetime | Istante assoluto di scatto (UTC). Ha precedenza su delaySeconds. |
callbackUrl |
string | URL http(s) da chiamare. Obbligatorio. |
method |
string | Metodo HTTP (default POST). |
headers |
object | Header HTTP custom inviati col callback. |
payload |
any (JSON) | Body inviato al callback (ignorato per GET/HEAD). |
maxRetries |
number | Tentativi massimi prima del dead-letter (default 5). |
idempotencyKey |
string | Chiave di dedup propagata al ricevitore (generata se assente). |
correlationId |
string | Id di correlazione per il tracciamento end-to-end. |
recurrenceSeconds |
number | Ricorrenza a intervallo (vedi sotto). |
scheduleTimes |
string[] | Ricorrenza di calendario: orari "HH:mm" (vedi sotto). |
scheduleDaysOfWeek |
number[] | Giorni ammessi (0=Domenica .. 6=Sabato). |
endAtUtc |
datetime | Stop ricorrenza: nessuna occorrenza oltre questa data. |
maxOccurrences |
number | Stop ricorrenza: numero massimo di occorrenze. |
Ricorrenza
Un job può ripetersi automaticamente: al termine di un'occorrenza (sia in caso di successo sia di dead-letter — la serie continua anche dopo un errore) ne viene creata la successiva. Tutte le occorrenze condividono un seriesId e hanno un occurrenceNumber progressivo.
A intervallo
recurrenceSeconds (minimo 60): l'occorrenza successiva scatta a cadenza fissa rispetto a quella pianificata.
{
"name": "Ping ogni ora",
"callbackUrl": "https://example.com/ping",
"recurrenceSeconds": 3600
}
A calendario (giornaliera / settimanale)
scheduleTimes (uno o più orari "HH:mm") ed eventualmente scheduleDaysOfWeek. Gli orari sono ora di parete italiana (Europe/Rome) con gestione automatica dell'ora legale; il primo scatto è la prossima occorrenza utile.
// Tutti i giorni alle 10:00 e alle 15:00
{
"name": "Sync giornaliera",
"callbackUrl": "https://example.com/sync",
"scheduleTimes": ["10:00", "15:00"]
}
// Lunedì e martedì alle 11:00, max 10 occorrenze
{
"callbackUrl": "https://example.com/report",
"scheduleTimes": ["11:00"],
"scheduleDaysOfWeek": [1, 2],
"maxOccurrences": 10
}
La cadenza è fissa: anche se un'esecuzione viene ritardata dai retry, l'occorrenza successiva è ancorata all'orario pianificato (niente deriva). Se il poller è stato fermo, riprende dalla prossima occorrenza futura senza recuperare gli arretrati.
Retry e dead-letter
Su fallimento (errore HTTP o eccezione) il tentativo viene contato e il job riprogrammato con backoff esponenziale (2^(tentativi-1) minuti, fino a un massimo di 1 ora). Superato maxRetries il job passa a DeadLetter e viene loggato come errore (utile per gli alert).
Stati
| Stato | Significato |
|---|---|
Pending |
In attesa di scattare. |
Firing |
Preso in carico dal poller, invio in corso. |
Done |
Callback eseguito con successo. |
Cancelled |
Annullato via DELETE (era Pending). |
DeadLetter |
Falliti tutti i tentativi. |
I job in stato terminale (Done, Cancelled, DeadLetter) vengono ripuliti automaticamente dopo 30 giorni.
Sicurezza del callback
Ogni callback include header aggiuntivi:
Contit-Idempotency-Key— chiave di idempotenza dell'occorrenza.Contit-Correlation-Id— se valorizzato sul job.Contit-Signature— firma HMAC-SHA256 del body nel formatot={timestamp},s={signature}, presente solo se sul servizio è configurato un secret di firma. Verificala lato ricevitore ricomputandoHMAC-SHA256(secret, "{timestamp}.{body}").
SDK
var client = new ContitClient(apiKey);
// One-shot tra 1 ora
var job = await client.ScheduledJob.Create(new CreateScheduledJobRequest
{
Name = "Reminder ordine",
DelaySeconds = 3600,
CallbackUrl = "https://example.com/hooks/reminder",
Payload = JToken.Parse("""{ "orderId": "abc123" }""")
});
// Ricorrente: ogni giorno alle 10:00 e 15:00
await client.ScheduledJob.Create(new CreateScheduledJobRequest
{
Name = "Sync giornaliera",
CallbackUrl = "https://example.com/sync",
ScheduleTimes = new List<string> { "10:00", "15:00" }
});
// Ispezione e annullamento
var pending = await client.ScheduledJob.Get(status: ScheduledJobStatus.Pending);
await client.ScheduledJob.Cancel(job.Id);
Dashboard
Nel pannello, sotto Impostazioni → Sviluppatore → Scheduler, trovi l'elenco dei job (filtrabile per stato), il dettaglio con payload/header/esito ed il pulsante Nuovo job per crearli manualmente — incluso il builder visuale per la pianificazione giornaliera/settimanale a uno o più orari.