V4 Webhooks

What’s a Webhook?

A webhook is a push-style HTTP callback that Frame.io fires as soon as something interesting happens in your account (e.g., a new file finishes transcoding, a comment is added, a project is created). Instead of polling the API, you supply a public HTTPS URL; Frame.io sends a JSON payload to that URL in real time so you can:

Sync metadata to an external DAM/MAM
Populate Slack channels or ticket systems

For more on what a webhook is, and what it does, see https://docs.webhook.site/.

Endpoint Overview

OperationEndpointDetails
Create a webhookPOST /v4/accounts/{account_id}/workspaces/{workspace_id}/webhooksBody with name, url, events[]
List all webhooks for a workspaceGET /v4/accounts/{account_id}/workspaces/{workspace_id}/webhooksSupports pagination
Show one webhookGET /v4/webhooks/{webhook_id}Returns signing secret only at creation time
Update a webhookPATCH /v4/webhooks/{webhook_id}Change url, events, or is_active
Delete a webhookDELETE /v4/webhooks/{webhook_id}Immediately stops deliveries

Authentication — All V4 endpoints require an OAuth 2.0 access token obtained through the Adobe Developer Console. Legacy developer tokens and JWTs are not accepted.

Updates to Webhooks in Frame V4

  • Added Account ID to the payload
  • Renamed Team ID to be Workspace ID
  • Split asset webhooks to file and folder webhooks

Webhook Event Subscriptions

When creating and updating webhooks identify which events you’re interested in. Choose as few or as many as you’d like, note that the experience is better if you subscribe to fewer events, splitting up your webhooks logically with different naming schemes and different endpoints so that you can model your business logic on the receiving end to do less filtering and routing in shared functions.

Event scope — All events are scoped to the Workspace provided during the creation of the webhook. This means events will be sent for actions taken in all projects in that Workspace.

Projects

EventDescription
project.createdA new Project has been created
project.updatedA Project’s settings are updated
project.deletedA Project has been deleted

Files

EventDescription
file.createdA File has been created in Frame.io. This does trigger before the file has actually been uploaded though so it will likely trigger before the file is fully uploaded - so keep that in mind depending on what you intend to do once you receive this event
file.readyAll transcodes have completed, after an file has been uploaded and processed
file.updatedA Files name, or other file information is changed
file.deletedA File is deleted (manually or otherwise)
file.upload.completedA File has been uploaded
file.versionedA File version has been created

Folders

EventDescription
folder.createdA new Folder has been created
folder.updatedA Folder’s settings are updated
folder.deletedA Folder has been deleted

Comments

EventDescription
comment.createdA new Comment or Reply has been created
comment.updatedA Comment has been updated
comment.deletedA Comment was deleted
comment.completedA Comment was marked as completed
comment.uncompletedA Comment was marked as uncompleted

Metadata

EventDescription
metadata.value.updatedMetadata fields updated for an asset

Collections

EventDescription
collection.createdA new Collection has been created
collection.updatedA Collection has been updated
collection.deletedA Collection was deleted

Custom Fields

EventDescription
customfield.createdA new custom field has been created
customfield.updatedA custom field has been updated
customfield.deletedA custom field was deleted

Shares

EventDescription
share.createdA new Share has been created
share.updatedA Share has been updated
share.deletedA Share was deleted
share.viewedA Share was viewed

Webhook Message Payload

All webhook payloads contain a type field, indicating the type of event that triggered a webhook, as well as a resource object. The resource object specifies the type and ID of the resource related to this event.

Example Payload

1{
2 "account": {
3 "id": "6f70f1bd-7e89-4a7e-b4d3-7e576585a181"
4 },
5 "project": {
6 "id": "7e46e495-4444-4555-8649-bee4d391a997"
7 },
8 "resource": {
9 "id": "d3075547-4e64-45f0-ad12-d075660eddd2",
10 "type": "file"
11 },
12 "type": "file.ready",
13 "user": {
14 "id": "56556a3f-859f-4b38-b6c6-e8625b5da8a5"
15 },
16 "workspace": {
17 "id": "378fcbf7-6f88-4224-8139-6a743ed940b2"
18 }
19}

In the above example of an file.created event, the resource.id indicates the id of the newly created Asset. Additionally, workspace, project, and user objects are included. These resource identifiers indicate the team.id, project.id and user.id of the resource that the webhook relates to, and can be used to filter events on the receiving end of the incoming webhook without having to resort to making an API call to look up the resource. If you’ve implemented any sort of caching of those resources, you can also perform a local look up against your cache without resorting an additional API call.

We do not include any additional information beyond the resource ID about the subscribed resource.

If your application requires additional information or context, we recommend making an API call to look-up more information about the resources being referenced.

Security

By default, all webhooks have a signing key. This non-configurable signing secret can be used to verify that the request originates from Frame.io.

The response payload for the webhook you’ve configured includes the signing secret specific to this webhook. This secret is only provided in this initial webhook create response, so store it somewhere safe in your secrets storage or environment variables. Use it later to verify the webhook is coming directly from our servers and was not intercepted or manipulated in any way.

Verifying Webhook Signatures

To guard an integration against man-in-the-middle and replay attacks it is essential to verify the signature of the webhook payload. Verification ensures that webhook payloads were actually sent by Frame.io and payload content has not been modified in transport.

Included with the POST request are the following HTTP headers:

Header NameDescriptionExample
X-Frameio-Request-TimestampThe timestamp the request was sent1604004499
X-Frameio-SignatureThe compute webhook signaturev0=a77ce6856e609c884575c2fd211d07a9ad1c3f72e19c06ff710e8f086ffca883
user-agent: "Frame.io V4 API"User agent in the header for v4
user-agent: "Frame.io Legacy API"User agent in the header for Legacy
Python
1import hmac
2import hashlib
3
4def verify_signature(curr_time, req_time, signature, body, secret):
5 """
6 Verify Webhook signature
7 :Args:
8 curr_time (float): Current epoch time
9 req_time (float): Request epoch time
10 signature (str): Signature provided by the Frame.io API for the given request
11 body (str): Webhook body from the received POST
12 secret (str): The secret for this Webhook that you saved when you first created it
13 """
14 if int(curr_time) - int(req_time) < 500:
15 message = 'v0:{}:{}'.format(req_time, body)
16 calculated_signature = 'v0={}'.format(hmac.new(
17 bytes(secret, 'latin-1'),
18 msg=bytes(message, 'latin-1'),
19 digestmod=hashlib.sha256).hexdigest())
20 if calculated_signature == signature:
21 return True
22 return False

The timestamp is the system time from Frame.io’s systems when the outbound webhook is sent. This can be used to prevent replay attacks. We recommended verifying this time is within 5 minutes of local time.

The signature is a HMAC SHA256 hash using the signing key provided when the Webhook is first created.

Follow these steps to verify the signature:

1

Extract the signature

Extract the signature from the HTTP headers.

2

Create message to sign

Create a message to sign by combining the version, delivery time, and request body: v0:timestamp:body.

3

Compute HMAC SHA256

Compute the HMAC SHA256 signature using your signing secret.

4

Compare signatures

Compare your computed signature with the provided one!

The provided signature is prefixed with v0=. Currently Frame.io only has this one version for signing requests. Be sure this prefix is prepended to your computed signature.

Retries and Logging

Retry Policy
  • Five total attempts (initial + 4 retries)
  • Exponential back-off starting at 15 s (+ jitter)
  • A non-2xx status or >5 second timeout triggers the retry
Failure Logging

Frame.io keeps a failure log with: webhook_id, account_id, event_type, resource_id, user_id.

Changes to Webhooks

Webhooks created in Legacy transfer to V4 with the following changes.

1

URL Structure Change

When creating a new webhook resource the team_id is no longer provided in the JSON payload, but that is instead in the path parameter of the URL: https://api.frame.io/v4/accounts/:account_id/workspaces/:workspace_id/webhooks.

2

API Structure Updates

Due to changes in API structure, endpoints, and authentication methods any existing code for incoming webhooks that makes subsequent calls to the Frame.io API for enrichment and look-up of resources require updating.

3

Event Split

Since the asset webhooks have been split to Files and Folders, any webhooks coming from Legacy with asset events need to be updated to have the appropriate File and Folder events.

Webhook Tutorial

Step 1: Setup receiving end (done first so that you know what your URL will be)

Here, we’re using webhook.site which allows you to easily spin up a single-use webhook receiver that can be used for inspecting payloads, sending basic responses without any actual business logic. When you first navigate to https://webhook.site, a unique webhook endpoint is created for you which you can copy right away to use.

This URL is unique to your session.

Step 1 Example

Step 2: Choose the event(s) you want to subscribe to

For this tutorial, we’ll keep it simple and setup this webhook to just subscribe to file.created events. The JSON payload we’ll use for the webhook creation will be as follows.

1{
2 "data": {
3 "name": "asset.created sample webhook",
4 "events": ["file.created"],
5 "url": "https://webhook.site/c05f2216-9558-4816-bc09-f77ee7b9de40"
6 }
7}

Step 3: Create a webhook resource using Postman

Using Postman, make an API call to create the webhook resource, supplying the webhook.site endpoint in the payload.

Step 4: Test!

Now that you’ve created the webhook subscription and have an endpoints setup to receive webhooks, it’s time to test it out by triggering the first webhook by performing the appropriate action that would cause it to fire!

Since our sample was set up to trigger on the file.created trigger, we’ll go ahead and upload a new asset into any Project within the corresponding Account and Workspace that the webhook was set up in.

Step 4 Example

Additional Resources