EN IT

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 formato t={timestamp},s={signature}, presente solo se sul servizio è configurato un secret di firma. Verificala lato ricevitore ricomputando HMAC-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.