> This page is for Plataforma, version Herdado.
> 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
> - Herdado: 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.

# Visão geral dos webhooks

<Info title="Aplicativo de exemplo">
  Se quiser criar seu próprio consumidor para webhooks do Frame.io, fique à vontade para usar e estender nosso [aplicativo de exemplo no GitHub](https://github.com/Frameio/webhooks-example-app).
</Info>


## Introdução




Os webhooks oferecem uma maneira de aproveitar eventos que ocorrem dentro do Frame.io em notificações que podem ser enviadas para sistemas externos para processamento, callbacks de API e, por fim, automação do fluxo de trabalho.




## Configuração

Os webhooks podem ser configurados na [área de webhooks do nosso site para desenvolvedores](https://developer.frame.io/app/webhooks).Um webhook requer:
* Nome — será exibido apenas no site para desenvolvedores.
* URL — onde entregar eventos.
* Equipe — a qual equipe este webhook será adicionado.
* Eventos — qual evento, ou eventos, devem acionar o webhook.




## Eventos compatíveis




Um único webhook pode se inscrever para receber qualquer número dos seguintes eventos:





### Projetos




| Evento | Acionador |
| ---------- | ---------- |
| `project.created` | Um novo projeto é criado |
| `project.updated` | As configurações de um projeto são atualizadas |
| `project.deleted` | Um projeto é excluído |




### Ativos



| Evento | Acionador |
| ---------- | ---------- |
| `asset.created` | Um recurso é adicionado/criado inicialmente no Frame.io, mas provavelmente antes de ter sido totalmente carregado |
| `asset.copied` | Um ativo foi copiado |
| `asset.updated` | A descrição, nome ou outras informações de arquivo de um ativo são alteradas |
| `asset.deleted` | Um ativo é excluído (manualmente ou de outra forma) |
| `asset.ready` | Todas as transcodificações foram concluídas após um ativo ter sido carregado e processado |
| `asset.label.updated` | O rótulo de status de um ativo é definido, alterado ou removido |
| `asset.versioned` | Um ativo tem controle de versão |



<Warning title="Controle de versão de ativo">
  Quando o evento `asset.versioned` é disparado, você recebe um conteúdo com o ID do ativo que teve controle de versão, não a própria pilha de versões.Portanto, se você espera conseguir passar esse `id` para outra função pensando que é o id da pilha de versões, você vai ter que procurar e rastrear esse recurso 'principal' em particular primeiro.
</Warning>

<Warning title="Atualizações de rótulo de ativo">
  O evento `asset.label.updated` não será disparado quando o rótulo de status for alterado por meio de uma chamada `PUT` para o ponto de acesso `/v2/assets/:id` via API pública (`BES-408`).No entanto, ele será disparado quando o rótulo de status for atualizado usando qualquer aplicativo nativo e integrações do Frame.io (Web, iOS, Premiere, After Effects, FCPX, etc).
</Warning>


### Comentários



| Evento | Acionador |
| ---------- | ---------- |
| `comment.created` | Um novo comentário ou resposta é criado |
| `comment.updated` | Um comentário é editado |
| `comment.deleted` | Um comentário é excluído |
| `comment.completed` | Um comentário é concluído |
| `comment.uncompleted` | Um comentário é marcado como não concluído |




### Links de revisão



| Evento | Acionador |
| ---------- | ---------- |
| `reviewlink.created` | Um novo link de revisão é criado |



### Colaboradores




| Evento | Acionador |
| ---------- | ---------- |
| `collaborator.created` | Um usuário colaborador foi adicionado à sua conta |
| `collaborator.deleted` | Um usuário colaborador foi removido da sua conta |




### Membros da equipe



| Evento | Acionador |
| ---------- | ---------- |
| `teammember.created` | Um membro da equipe foi adicionado à sua conta |
| `teammember.deleted` | Um membro da equipe foi removido da sua conta |




## Conteúdo




O Frame.io entrega um conteúdo JSON para o ponto de acesso do webhook especificado.Este é um exemplo de conteúdo para um evento *asset.created*:





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

Todos os conteúdos contêm um campo `type`, indicando o tipo de evento que está ocorrendo, bem como um objeto `resource`.O objeto `resource` especifica o `type` e o `id` do recurso relacionado a este evento.No exemplo acima de um evento *asset.created*, este seria o `id` para o ativo recém-criado.Além disso, os objetos `user` e `team` estão incluídos.Estes fazem referência ao usuário que acionou o evento e ao contexto da equipe para o recurso.Além do contexto imediato do usuário e da equipe, **não incluímos informações adicionais sobre o recurso assinado**.Se o aplicativo precisar de informações ou contexto adicionais, recomendamos usar nossa API HTTP para fazer solicitações de acompanhamento.

## Novas tentativas




Se ocorrer um erro (resposta de código de status diferente de 200) ou tempo-limite durante a entrega do webhook para seu serviço, o conteúdo será tentado novamente três vezes, totalizando quatro tentativas de entrega.




## Segurança




Por padrão, todos os webhooks são fornecidos com uma chave de assinatura.Isso não é configurável.Essa chave pode ser usada para verificar se a solicitação se origina do Frame.io.




### Verificar assinaturas de webhook




Para proteger uma integração contra ataques &quot;man-in-the-middle&quot; e de repetição, é essencial verificar as assinaturas de webhooks.A verificação garante que os conteúdos de webhook foram realmente enviados pelo Frame.io e que o conteúdo não foi modificado durante o transporte.

Incluídos na solicitação `POST` estão os seguintes cabeçalhos:
| Nome | Descrição |
| ---------- | ---------- |
| `X-Frameio-Request-Timestamp` | A hora da entrega do webhook |
| `X-Frameio-Signature` | A assinatura computada |
**O carimbo de data e hora** é a hora da entrega dos sistemas do Frame.io.Isso pode ser usado para impedir ataques de repetição.Recomendamos verificar se essa hora está dentro de um intervalo de 5 minutos em relação à hora local.**A assinatura** é um hash HMAC SHA256 usando a chave de assinatura fornecida quando o webhook é criado pela primeira vez.

Siga estas etapas para verificar a assinatura:




1. Extraia a assinatura dos cabeçalhos HTTP
2. Crie uma mensagem para assinar combinando a versão, a hora da entrega e o corpo da solicitação: `v0:timestamp:body`
3. Compute a assinatura HMAC SHA-256 utilizando seu segredo de assinatura._Observação: A assinatura fornecida tem o prefixo `v0=`.Atualmente, o Frame.io possui apenas essa versão para assinar solicitações.Certifique-se de que este prefixo seja anexado à sua assinatura computada._
4. Compare!





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