> This page is for Piattaforma, version Versione precedente.
> For other versions, use one of these documentation indexes:
> - V4 (default): https://next.developer.frame.io/platform/v4/llms.txt
> - V4 sperimentale: https://next.developer.frame.io/platform/v4-experimental/llms.txt
> - Versione precedente: https://next.developer.frame.io/platform/v2/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://next.developer.frame.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://next.developer.frame.io/_mcp/server.

# Panoramica sui webhook

<Info title="Applicazione di esempio">
  Se vuoi creare un consumer personale per i webhook di Frame.io, puoi scaricare ed estendere la nostra [app di esempio su GitHub](https://github.com/Frameio/webhooks-example-app).
</Info>


## Introduzione




I webhook consentono di sfruttare gli eventi che si verificano all'interno di Frame.io trasformandoli in notifiche che possono essere inviate a sistemi esterni per l'elaborazione, i callback API e l'automazione dei flussi di lavoro.




## Configurazione

I webhook possono essere configurati nella [sezione Webhooks del nostro sito per sviluppatori](https://developer.frame.io/app/webhooks). Un webhook richiede:
* Nome: viene visualizzato solo nel sito per sviluppatori.
* URL: dove consegnare gli eventi.
* Team: a quale team verrà aggiunto il webhook.
* Eventi: l'evento o gli eventi che devono attivare il webhook.




## Eventi supportati




Un singolo webhook può sottoscrivere qualsiasi numero dei seguenti eventi:





### Progetti




| Evento | Trigger |
| ---------- | ---------- |
| `project.created` | Viene creato un nuovo progetto |
| `project.updated` | Vengono aggiornate le impostazioni di un progetto |
| `project.deleted` | Viene eliminato un progetto |




### Risorse



| Evento | Trigger |
| ---------- | ---------- |
| `asset.created` | Viene aggiunta o creata per la prima volta una risorsa in Frame.io, ma probabilmente prima che la risorsa sia completamente caricata |
| `asset.copied` | È stata copiata una risorsa |
| `asset.updated` | La descrizione, il nome o altre informazioni di file di una risorsa vengono modificati |
| `asset.deleted` | Una risorsa viene eliminata (manualmente o in altro modo) |
| `asset.ready` | Tutte le transcodifiche sono state completate, dopo che una risorsa è stata caricata ed elaborata |
| `asset.label.updated` | L'etichetta di stato di una risorsa viene impostata, cambiata o rimossa |
| `asset.versioned` | Una risorsa viene sottoposta al controllo delle versioni |



<Warning title="Controllo delle versioni delle risorse">
  Quando viene attivato l'evento `asset.versioned`, ricevi un payload con l'ID della risorsa di cui è stato eseguito il controllo delle versioni, non dello stack di versioni stesso. Quindi, se pensi di poter passare questo `id` a un'altra funzione pensando che sia l'ID dello stack di versioni, devi prima cercare e rintracciare quella particolare risorsa &quot;padre&quot;.
</Warning>

<Warning title="Aggiornamenti delle etichette delle risorse">
  L'evento `asset.label.updated` non si attiva quando l'etichetta di stato viene cambiata tramite una chiamata `PUT` all'endpoint `/v2/assets/:id` tramite l'API pubblica (`BES-408`). Si attiva, invece, quando l'etichetta di stato viene aggiornata utilizzando app e integrazioni native di Frame.io (web, iOS, Premiere, After Effects, FCPX ecc.).
</Warning>


### Commenti



| Evento | Trigger |
| ---------- | ---------- |
| `comment.created` | Viene creato un nuovo commento o una nuova risposta |
| `comment.updated` | Un commento viene modificato |
| `comment.deleted` | Un commento viene eliminato |
| `comment.completed` | Un commento viene completato |
| `comment.uncompleted` | Il completamento di un commento viene annullato |




### Link di revisione



| Evento | Trigger |
| ---------- | ---------- |
| `reviewlink.created` | Viene creato un nuovo link di revisione |



### Collaboratori




| Evento | Trigger |
| ---------- | ---------- |
| `collaborator.created` | È stato aggiunto un collaboratore al tuo account |
| `collaborator.deleted` | È stato rimosso un collaboratore dal tuo account |




### Membri del team



| Evento | Trigger |
| ---------- | ---------- |
| `teammember.created` | È stato aggiunto un membro del team al tuo account |
| `teammember.deleted` | È stato rimosso un membro del team dal tuo account |




## Payload




Frame.io fornisce un payload JSON all'endpoint del webhook specificato. Ecco un esempio di payload per un evento *asset.created*:





```json
{
  "type": "asset.created",
  "resource": {
    "type": "asset",
    "id": "<asset-id>"
  },
  "user": {
    "id": "<user-id>"
  },
  "team": {
    "id": "<team-id>"
  }
}
```

Tutti i payload contengono un campo `type`, che indica il tipo di evento in corso, oltre a un oggetto `resource`. L'oggetto `resource` specifica il tipo (`type`) e l'`id` della risorsa correlata a questo evento. Nell'esempio precedente di un evento *asset.created*, questo sarebbe l'`id` della risorsa appena creata. Inoltre, sono inclusi gli oggetti `user` e `team`. Questi fanno riferimento all'utente che ha attivato l'evento e al contesto del team per la risorsa. Oltre al contesto immediato di utente e team, **non includiamo informazioni aggiuntive sulla risorsa sottoscritta**. Se l'applicazione richiede informazioni o contesto aggiuntivi, consigliamo di utilizzare la nostra API HTTP per effettuare richieste di follow-up.

## Nuovi tentativi




Se si verifica un errore (risposta con codice di stato diverso da 200) o un timeout durante la consegna del webhook al servizio, il payload viene ripetuto tre volte, per un totale di quattro tentativi di consegna.




## Sicurezza




Per impostazione predefinita, tutti i webhook sono forniti con una chiave di firma. Non è configurabile. Questa chiave può essere utilizzata per verificare che la richiesta provenga da Frame.io.




### Verifica delle firme dei webhook




Per proteggere un'integrazione dagli attacchi man-in-the-middle e replay, è essenziale verificare le firme dei webhook. La verifica garantisce che i payload dei webhook siano stati effettivamente inviati da Frame.io e che il contenuto del payload non sia stato modificato durante il trasporto.

Nella richiesta `POST` sono incluse le seguenti intestazioni:
| Nome | Descrizione |
| ---------- | ---------- |
| `X-Frameio-Request-Timestamp` | L'orario di consegna del webhook |
| `X-Frameio-Signature` | La firma calcolata |
**La marca temporale** è l'orario di consegna dai sistemi di Frame.io. Può essere utilizzata per evitare attacchi di tipo replay. Si consiglia di verificare che questo orario rientri nei 5 minuti dall'orario locale. **La firma** è un hash HMAC SHA256 che utilizza la chiave di firma fornita quando il webhook viene creato per la prima volta.

Segui questi passaggi per verificare la firma:




1. Estrai la firma dalle intestazioni HTTP
2. Crea un messaggio da firmare combinando la versione, l'ora di consegna e il corpo della richiesta: `v0:timestamp:body`
3. Calcola la firma HMAC SHA256 utilizzando il secret di firma. Nota: la firma fornita ha il prefisso `v0=`. Al momento Frame.io ha solo questa versione per firmare le richieste. Assicurati che questo prefisso venga anteposto alla firma calcolata.
4. Confronta!





**`Python`**

```python title="Python"
import hmac
import hashlib

def verify_signature(curr_time, req_time, signature, body, secret):
    """
    Verify Webhook signature
    :Args:
        curr_time (float): Current epoch time
        req_time (float): Request epoch time
        signature (str): Signature provided by the Frame.io API for the given request
        body (str): Webhook body from the received POST
        secret (str): The secret for this Webhook that you saved when you first created it
    """
    if int(curr_time) - int(req_time) < 500:
        message = 'v0:{}:{}'.format(req_time, body)
        calculated_signature = 'v0={}'.format(hmac.new(
            bytes(secret, 'latin-1'),
            msg=bytes(message, 'latin-1'),
            digestmod=hashlib.sha256).hexdigest())
        if calculated_signature == signature:
            return True
    return False
```





```js
const crypto = require('crypto');

// Capture the signature, secret, timestamp and payload from a new webhook event:
const 
signature = 'v0=a77ce6856e609c884575c2fd211d07a9ad1c3f72e19c06ff710e8f086ffca883', 
secret = 'yxSE59T0gtZOFZxw6UhLwTkhd2m8ntNSdSWnApQ0xOnMEzSoXbD8sGFP4bzb7MbS',
timestamp = 1604004499,  // UNIX timestamp in seconds
payload = {
	"project": {
		"id": "f348e9f4-f142-42f9-b3bf-478d93f0feb4"
	},
	"resource": {
		"id": "6aad9151-c216-4d6f-b5e9-530df551a426",
		"type": "asset"
	},
	"team": {
		"id": "aa891687-4b1e-4150-9b6d-9e4911c5b436"
	},
	"type": "asset.label.updated",
	"user": {
		"id": "59c9ade1-311b-4c3b-8231-b9d88e9a1a85"
	}
},
body = JSON.stringify(payload),

// Validate that caught payload is not older than 5 minutes
currentTimeUTC = (new Date()).getTime(),
currentTimestamp = currentTimeUTC / 1000, // JavaScript uses milliseconds whereas Unix Time is in seconds.
minutes = 5, 
expired = (currentTimestamp - timestamp) > minutes*60
hmac1 = crypto.createHmac('sha256', secret),
generateSignature = hmac1.update(`v0:${timestamp}:${body}`).digest('hex')

// Evaluates to true if the webhook is verified
console.log(!expired && signature === `v0=${generateSignature}`)
```





```go
// Full Go sample code: https://github.com/Frameio/webhooks-example-app/blob/master/main.go

func handler(w http.ResponseWriter, r *http.Request) {
	out, err := httputil.DumpRequest(r, true)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	log.Println(string(out))

	// Verify the message has been delivered in the last 5 minutes.
	timestampStr := r.Header.Get("X-Frameio-Request-Timestamp")
	timestamp, err := strconv.ParseInt(timestampStr, 10, 64)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	if time.Since(time.Unix(timestamp, 0)) > 5*time.Minute {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	// Verify request signature.
	expected := r.Header.Get("X-Frameio-Signature")
	signature, _ := computeSignature(r, timestamp, secretKey)
	if expected != signature {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	var event *Event
	decoder := json.NewDecoder(r.Body)
	err = decoder.Decode(&event)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// Handle webhook here.
	log.Println(event.ID)

	w.WriteHeader(http.StatusOK)
}

// The request includes headers to enable the recipient to validate
// that the request is from Frame.io and that it's been delivered within
// the expected time range. To learn more about how this works, take a
// look at our docs https://docs.frame.io/docs/webhooks#section-security.
func computeSignature(r *http.Request, timestamp int64, secret string) (string, error) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return "", err
	}
	copy := body[:]
	r.Body = ioutil.NopCloser(bytes.NewReader(copy))

	msg := fmt.Sprintf("%s:%d:%s", version, timestamp, string(body))

	key := []byte(secret)
	h := hmac.New(sha256.New, key)
	h.Write([]byte(msg))

	result := fmt.Sprintf("%s=%s", version, hex.EncodeToString(h.Sum(nil)))

	return result, nil
}
```