Ir para a navegação

Visão geral dos webhooks

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.

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.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

EventoAcionador
project.createdUm novo projeto é criado
project.updatedAs configurações de um projeto são atualizadas
project.deletedUm projeto é excluído

Ativos

EventoAcionador
asset.createdUm recurso é adicionado/criado inicialmente no Frame.io, mas provavelmente antes de ter sido totalmente carregado
asset.copiedUm ativo foi copiado
asset.updatedA descrição, nome ou outras informações de arquivo de um ativo são alteradas
asset.deletedUm ativo é excluído (manualmente ou de outra forma)
asset.readyTodas as transcodificações foram concluídas após um ativo ter sido carregado e processado
asset.label.updatedO rótulo de status de um ativo é definido, alterado ou removido
asset.versionedUm ativo tem controle de versão
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.

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).

Comentários

EventoAcionador
comment.createdUm novo comentário ou resposta é criado
comment.updatedUm comentário é editado
comment.deletedUm comentário é excluído
comment.completedUm comentário é concluído
comment.uncompletedUm comentário é marcado como não concluído
EventoAcionador
reviewlink.createdUm novo link de revisão é criado

Colaboradores

EventoAcionador
collaborator.createdUm usuário colaborador foi adicionado à sua conta
collaborator.deletedUm usuário colaborador foi removido da sua conta

Membros da equipe

EventoAcionador
teammember.createdUm membro da equipe foi adicionado à sua conta
teammember.deletedUm 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:

{
"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 “man-in-the-middle” 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:

NomeDescrição
X-Frameio-Request-TimestampA hora da entrega do webhook
X-Frameio-SignatureA 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
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
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}`)
// 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
}