> This page is for Plataforma, version Heredado.
> For other versions, use one of these documentation indexes:
> - V4 (default): https://next.developer.frame.io/platform/v4/llms.txt
> - V4 experimental: https://next.developer.frame.io/platform/v4-experimental/llms.txt
> - Heredado: 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.

# Información general sobre los webhooks

<Info title="Aplicación de ejemplo">
  Si desea crear su propio consumidor para los webhooks de Frame.io, puede descargar y ampliar nuestra [aplicación de ejemplo en GitHub](https://github.com/Frameio/webhooks-example-app).
</Info>


## Introducción




Los webhooks proporcionan una forma de aprovechar eventos que ocurren dentro de Frame.io en notificaciones que se pueden enviar a sistemas externos para el procesamiento, las devoluciones de llamadas API y, en última instancia, la automatización de flujos de trabajo.




## Configurar

Los webhooks pueden configurarse en el [área de webhooks de nuestro sitio para desarrolladores](https://developer.frame.io/app/webhooks). Un webhook requiere:
* Nombre: Se mostrará únicamente en el sitio para desarrolladores.
* URL: Lugar donde se entregarán los eventos.
* Equipo: A qué equipo se añadirá este webhook.
* Eventos: Qué evento o eventos deben activar el webhook.




## Eventos compatibles




Un solo webhook puede suscribirse a todos los siguientes eventos:





### Proyectos




| Evento | Activador |
| ---------- | ---------- |
| `project.created` | Se ha creado un nuevo proyecto |
| `project.updated` | Se ha actualizado la configuración de un proyecto |
| `project.deleted` | Se ha eliminado un proyecto |




### Activos



| Evento | Activador |
| ---------- | ---------- |
| `asset.created` | Un activo se añade/crea por primera vez en Frame.io, pero probablemente antes de que el activo se cargue completamente |
| `asset.copied` | Se ha copiado un activo |
| `asset.updated` | Se ha cambiado la descripción, el nombre u otra información de archivo de un activo |
| `asset.deleted` | Se ha eliminado un activo (manualmente o de otra forma) |
| `asset.ready` | Todas las transcodificaciones han finalizado después de que un archivo se haya cargado y procesado |
| `asset.label.updated` | La etiqueta de estado de un activo se ha establecido, cambiado o quitado |
| `asset.versioned` | Un activo tiene versiones |



<Warning title="Versiones de activos">
  Cuando se activa el evento `asset.versioned`, va a recibir una carga útil con el id del activo que tiene versiones, no del grupo de versiones en sí. Por lo tanto, si espera poder pasar ese `id` a otra función pensando que es el id del grupo de versiones, va a tener que buscar y localizar primero ese recurso &quot;principal&quot; en concreto.
</Warning>

<Warning title="Actualizaciones de etiquetas de activos">
  El evento `asset.label.updated` no se activará cuando la etiqueta de estado se cambie mediante una llamada `PUT` al punto final `/v2/assets/:id` a través de la API pública (`BES-408`). Sin embargo, se activará cuando la etiqueta de estado se actualice mediante cualquier aplicación e integración nativa de Frame.io (Web, iOS, Premiere, After Effects, FCPX, etc.).
</Warning>


### Comentarios



| Evento | Activador |
| ---------- | ---------- |
| `comment.created` | Se ha creado un nuevo comentario o una nueva respuesta |
| `comment.updated` | Se ha editado un comentario |
| `comment.deleted` | Se ha eliminado un comentario |
| `comment.completed` | Se ha completado un comentario |
| `comment.uncompleted` | Se ha marcado un comentario como no completado |




### Vínculos de revisión



| Evento | Activador |
| ---------- | ---------- |
| `reviewlink.created` | Se ha creado un vínculo de revisión nuevo |



### Colaboradores




| Evento | Activador |
| ---------- | ---------- |
| `collaborator.created` | Se ha añadido un colaborador a su cuenta |
| `collaborator.deleted` | Se ha quitado un colaborador de su cuenta |




### Integrantes del equipo



| Evento | Activador |
| ---------- | ---------- |
| `teammember.created` | Se ha añadido un integrante del equipo a su cuenta |
| `teammember.deleted` | Se ha quitado un integrante del equipo de su cuenta |




## Carga útil




Frame.io entrega una carga útil JSON al punto final del webhook especificado. Este es un ejemplo de carga útil para un evento *asset.created*:





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

Todas las cargas útiles contienen un campo `type`, que indica el tipo de evento que se produce, así como un objeto `resource`. El objeto `resource` especifica el `type` y el `id` del recurso relacionado con el evento. En el ejemplo anterior de un evento *asset.created*, este sería el `id` del nuevo activo creado. Además, se incluyen objetos `user` y `team`. Estos hacen referencia al usuario que activó el evento y al contexto del equipo para el recurso. Aparte del contexto inmediato de usuario y equipo, **no incluimos información adicional sobre el recurso suscrito**. Si la aplicación requiere información o contexto adicional, recomendamos usar nuestra API HTTP para realizar solicitudes de seguimiento.

## Reintentos




Si se produce un error (respuesta con código de estado distinto de 200) o se agota el tiempo de espera al entregar el webhook a su servicio, la carga útil se reintentará tres veces, para un total de cuatro intentos de entrega.




## Seguridad




De forma predeterminada, todos los webhooks se proporcionan con una clave de firma. Esta clave no se puede configurar.Puede usarse para verificar que la solicitud procede de Frame.io.




### Verificar firmas de webhook




Para proteger una integración contra ataques de intermediario y de repetición, es esencial verificar las firmas de webhook. La verificación garantiza que las cargas útiles de webhook las haya enviado realmente Frame.io y que el contenido no se haya modificado durante el transporte.

La solicitud `POST` incluye los siguientes encabezados:
| Nombre | Descripción |
| ---------- | ---------- |
| `X-Frameio-Request-Timestamp` | La marca de tiempo de la entrega del webhook |
| `X-Frameio-Signature` | La firma calculada |
**La marca de tiempo** es el momento de la entrega desde los sistemas de Frame.io. Puede usarse para evitar ataques de reproducción.Recomendamos verificar que esta hora esté dentro de un intervalo de 5 minutos con respecto a la hora local.**La firma** es un hash HMAC SHA-256 que usa la clave de firma proporcionada al crear el webhook por primera vez.

Siga estos pasos para verificar la firma:




1. Extraiga la firma de los encabezados HTTP
2. Cree un mensaje para firmarlo combinando la versión, la hora de entrega y el cuerpo de la solicitud: `v0:timestamp:body`
3. Calcule la firma HMAC SHA-256 con el secreto de firma._Nota: La firma proporcionada lleva el prefijo `v0=`. Actualmente, Frame.io solo tiene esta versión para firmar solicitudes.Asegúrese de añadir este prefijo al principio de la firma calculada._
4. Haga una comparación.





**`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
}
```