Vai al contenuto principale

Guida all’implementazione

Chiudi il lavoro quando la richiesta non c’è più.

Un’attività media sopravvive alla chiamata HTTP che l’ha creata, alla pagina che l’ha avviata e spesso al deploy che girava in quel momento. Tutto quello che segue serve a rendere sopportabile questo fatto.

La sfida

La connessione non è la transazione

Fra il premere invio e avere il file in mano ci sono quattro punti in cui si perde lavoro: una creazione andata in timeout che ritenti alla cieca, una callback che non riesci ad autenticare, un ciclo di polling che martella o che si arrende, e un URL di risultato scaduto mentre decidevi cosa farne.

01

Una creazione ritentata senza chiave è una seconda fattura

Idempotency-Key fa sì che la ripetizione di stessi modello, input e callback entro 24 ore restituisca l’attività originale invece di bloccare fondi due volte. Vale per una sola chiave API: una chiave gemella che rigioca lo stesso valore ottiene 409 e nessun ID attività.

02

Una callback non verificata è un endpoint aperto

Le consegne portano X-Webhook-Timestamp e un HMAC-SHA256 in Base64 in X-Webhook-Signature, calcolato su taskId.timestamp.sha256(raw_body). Verifica sui byte grezzi a tempo costante e rifiuta i timestamp vecchi prima di toccare lo stato dell’attività o della fatturazione.

03

Un polling senza tetto è un limite di frequenza che aspetta di succedere

recordInfo è un ripiego, non un trasporto. Parti da circa due secondi, cresci della metà ogni volta, fermati a quindici e smetti a una scadenza scelta da te e non dalla rete.

L’approccio

Un record durevole per attività, scritto prima di rispondere

Salva la chiave di idempotenza, il taskId e il tuo ID di correlazione nella stessa transazione che risponde a chi ti ha chiamato. Ogni evento successivo – una callback, un polling, un ritentativo, una domanda dell’assistenza – si risolve su quella riga, e una consegna duplicata diventa un aggiornamento invece di un secondo ordine.

Prima verifica, poi leggi. La firma copre i byte grezzi, quindi fai il parsing solo dopo che il confronto è passato.

Verify the callback before you trust it

import { createHash, createHmac, timingSafeEqual } from "node:crypto";

// rawBody is the exact bytes that arrived. Parse only to read the task ID:
// the digest is always computed over those raw bytes, never over a re-encode.
export function verifyDelivery(rawBody, headers, webhookKey) {
  const timestamp = headers["x-webhook-timestamp"];
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;

  const taskId = JSON.parse(rawBody).data.taskId;          // payload version 2
  const digest = createHash("sha256").update(rawBody).digest("hex");
  const expected = createHmac("sha256", webhookKey)
    .update(`${taskId}.${timestamp}.${digest}`)
    .digest("base64");

  const sent = Buffer.from(headers["x-webhook-signature"] ?? "", "utf8");
  const mine = Buffer.from(expected, "utf8");
  return sent.length === mine.length && timingSafeEqual(sent, mine);
}

Delivery headers

X-Webhook-Timestamp: 1788652800
X-Webhook-Signature: <base64>
X-Webhook-Payload-Version: 2

{
  "code": 200,
  "msg": "success",
  "request_id": "whd_01k3m8x9q2z4v7n5p6r8s0t1w3",
  "data": { "taskId": "job_01k3…", "state": "succeeded", "settled": true, … }
}

Contratto

Cosa conviene fissare fin dall’inizio

API media asincrona: attività e webhook — Cosa conviene fissare fin dall’inizio
Campo o headerValore o limite
Idempotency-Key24 h · scoped to one API key
X-Webhook-Signaturebase64(HMAC-SHA256)
signed stringtaskId.timestamp.sha256(raw_body)
X-Webhook-Payload-Version2 for new integrations

Scorri in orizzontale per vedere tutte le colonne.

Integrazione

Dalla prima chiamata alla produzione

  1. 01Genera e salva una chiave di idempotenza, poi fai POST createTask con quella e con callBackUrl che punta a un endpoint HTTPS sulla porta 80 o 443.
  2. 02Scrivi data.taskId nel tuo database prima di rispondere a chi ti ha chiamato: il 202 è l’unico posto in cui compare per primo.
  3. 03Accetta la callback, verifica la firma sul corpo grezzo, deduplica su request_id e aggiorna il tuo record.
  4. 04Ripiega su recordInfo con backoff esponenziale e copia output.assets nel tuo archivio prima che l’URL firmato scada.

Scelta

Quando questa strada è quella giusta

Scegli questa via se

  • Qualsiasi cosa produca media. Gli endpoint immagine, video e audio sono asincroni per costruzione.
  • Ti serve un record durevole di cosa è stato inviato, quanto doveva costare e come è finito.

Cerca altrove se

  • Stai chiamando un modello di testo. Quelli fanno streaming dentro la richiesta e si chiudono sull’uso misurato.
  • Vuoi che sia il browser a possedere il lavoro. Le chiavi stanno sul tuo server, e lì sta anche il record dell’attività.

Verifiche

Cosa puoi verificare in autonomia

Ogni risposta porta un request_id

La struttura della risposta è {code, msg, data, request_id} su ogni operazione /api/v1. Nella versione 2 del payload il request_id della consegna resta stabile fra un reinvio e l’altro, il che ne fa la chiave di deduplicazione naturale.

I blocchi si vedono nel record

cost con settled: false è la stima bloccata; settled: true è l’addebito finale. Il successo non supera mai il blocco, e un fallimento o una scadenza lo liberano per intero.

I fallimenti usano un vocabolario chiuso

errorCode è uno di nove valori stabili: invalid_request, unsupported_combination, content_rejected, rate_limited, upstream_unavailable, generation_failed, timeout, invalid_asset, upstream_failed. Tratta qualunque altra cosa come upstream_failed.

Prima del lancio

Comportamenti della piattaforma da confermare

  • Conserva la chiave di idempotenza insieme all’attività, non nella memoria del processo.
  • Rendi idempotente il gestore della callback: le consegne vengono ripetute per scelta.
  • Fissa una scadenza per il polling e fai scattare un avviso sulle attività che la superano.
  • Copia i file prima di expiresAt, e richiedi un URL nuovo invece di mettere in cache uno firmato.

Domande frequenti

Cosa ci chiedono su questa pagina

Ritenta la stessa richiesta con la stessa Idempotency-Key. Entro 24 ore e sulla stessa chiave API riottieni l’attività originale senza un secondo blocco. Non inviare mai un corpo nuovo dopo un timeout: quella è un’attività nuova, fatturata a parte.

Continua

Costruisci un flusso di attività che sopravvive a un deploy

Invia lavori media lunghi dal tuo server e portali a termine: chiavi di idempotenza, callback firmate, polling limitato e cinque stati.

Guide correlate