Creating Webhooks

Creating a webhook is a two-step process. You'll first need to set up how you want your webhook to behave through CreativeForce - what events should it listen to. After that, you'll set up your server to receive and manage the payload.

Setting up a Webhook

To set up a webhook, go to the “Studio settings” page via the menu in the top right corner, and click on "Developer" in the sub-menu to the right.

From there, click “Add webhook”. Webhooks require a few configuration options before you can make use of them. We'll go through each of these settings below here.

Set up


Payload URL

The Payload URL is the URL of the server that will receive the webhook requests.

  • This URL must be HTTPS

  • The URL can not be an IP (private IP as well as public IP)

The URL can not contain any of the following:

  • localhost

  • creativeforce.io

  • metadata.google

  • burpcollaborator.net

  • .local

  • .internal

  • ram.aliyuncs.com

  • fuf.me

  • localtest.me

  • ulh.us

Example of a good Payload URL

Examples of bad Payload URLs

Secret

When your secret key is set, Creative Force uses it to create a hash signature with each payload. This hash signature is passed along with each request in the headers as X-CF-Signature. See the section about validating the webhook later in this document.

The format of the secret key is Base64 RFC 2045

Events to trigger the webhook

Events are at the core of webhooks. The webhooks fire whenever a certain action is taken either by users or the Creative Force system. Your server (payload URL) will intercept and act upon these.

You can select from 4 different event groups:

Product Events

  • Imported

  • Reset

  • Deleted

  • StatusChanged

  • StyleguideChanged

  • CategoryChanged

  • Ignored

  • ColorReferenceMigrated

  • PropertiesUpdated

  • Activated

Sample Events

  • LocationChanged

  • Checked-in

  • Checked-out

User Events

  • UserLoggedIn

  • UserLoggedOut

  • PasswordChanged

  • AvatarUpdated

  • UserInvitationCompleted

  • UserUpdated

  • UserDeleted

  • UserInvitationDeleted

  • UserDisabled

  • UserEnabled

  • UserInvited

  • UserRe-Invited

Task Events

  • Completed

  • Started

  • ReadyToWork

  • Bypassed

  • Reset

  • Assigned

  • Unassigned

  • Rejected

  • Approved

When you're finished, click Save. Now that you created the webhook, it's time to set up our server to test the webhook.

Webhook Details

Delivery headers

HTTP POST payloads that are delivered to your webhook's configured URL endpoint will contain several special headers:

Header

Description

X-CF-Event

Name of the event that triggered the delivery. For example “Task completed”, “User created”.

X-CF-Signature

The HMAC hex digest of the response body. See the section about validating the webhook later in this document.

Webhook for “Product”

Key

Type

Description

Action

string

The action that was performed. Can be one of:

Imported, Reset, Deleted, StatusChanged, StyleGuideChanged, CategoryChanged, Ignored, ColorReferenceMigrated, PropertiesUpdated, Activated

Actor

object

The user or the actor that triggered the event. Notes: We will send UserID only, no user name.

PayloadId

string

ID of the payload (GUID)

EventGroupName

string

The event group triggered. Value: product

EventDatetimeUtc

long

Unix Timestamp (milliseconds) - the number of milliseconds since the Unix Epoch

JobId

string

ID of the job (GUID)

JobCode

string

Job code

ClientId

string

ID of the client (GUID).

ClientName

string

Client name

ProductId

string

ID of the product (GUID)

ProductCode

string

Product code

ProductStatusId

int

Product Unit Status ID

ProductStatusName

string

Product Unit Status Name

Webhook payload example when someone reset a Product:

{
"Action":"ProductReset",
"Actor":{
"UserId":"19e62847-f302-4e32-9e4a-b6d8c304fcbe"
},
"PayloadId":"c02f0f73-1706-4973-9a4d-38007d6685e9",
"EventGroupName":"product",
"EventDatetimeUtc":1595204418000,
"JobId":"d84eef10-c8a6-437a-9f95-0b58d2a3cb38",
"JobCode":"Summer 2020",
"ClientId":"2c856b2f-aded-4609-bf67-27009603b2b9",
"ClientName":"My Big client",
"ProductId":"7a782d57-b0bf-4743-8f02-2b1a1e229869",
"ProductCode":"AW2130146"
}

Webhook for “Sample”

Key

Type

Description

Action

string

The action that was performed. Can be one of:

LocationChanged, Checked-in, Checked-out

Actor

object

The user or the actor that triggered the event. Notes: We will send UserID only, no user name.

PayloadId

string

ID of the payload (GUID)

EventGroupName

string

The event group triggered. Value: sample

EventDatetimeUtc

long

Unix Timestamp (milliseconds) - the number of milliseconds since the Unix Epoch

SampleId

string

ID of the Sample (GUID)

LocationId

string

ID of the location (GUID)

LocationName

string

Location name

SubLocationId

string

ID of the sub-location (GUID)

SubLocationName

string

Sub-location name

Products

array

Array of ID of the product ID (GUID) and Product code.

If there is no product then return an empty array.

products: [{productId: "", productCode: ""},....}

Webhook payload example when someone check-in a sample:

{
"SampleId": "4666fcd0-0179-44d8-a66d-bc65a4acd968",
"LocationId": "ee410fb0-6a99-4b0e-a95e-83abe6aa5a5e",
"LocationName": "CF Center",
"SubLocationId": "00000000-0000-0000-0000-000000000000",
"SubLocationName": "",
"Products": [{
"ProductId": "ef0533fc-a11c-43a2-ab84-1c9bdabc9e7f",
"ProductCode": "ADROAN-830"
}
],
"PayloadId": "5385f2a9-88da-4c38-88a5-5f6f875ecb74",
"EventGroupName":"sample",
"EventDatetimeUtc": 1601969236991,
"Action": "Checked-in",
"Actor": {
"UserId": "205c0bbd-ea29-4250-87f5-bdb254a91075"
}
}



Webhook for “User”

Key

Type

Description

Action

string

The action that was performed. Can be one of:

UserLoggedIn, UserLoggedOut, PasswordChanged, AvatarUpdated, UserInvitationCompleted, UserUpdated, UserDeleted, UserInvitationDeleted, UserDisabled, UserEnabled, UserInvited, UserRe-Invited

Actor

object

The user or the actor that triggered the event. Notes: We will send UserID only, no user name.

PayloadId

string

ID of the payload (GUID)

EventGroupName

string

The event group triggered. Value: user

EventDatetimeUtc

long

Unix Timestamp (milliseconds) - the number of milliseconds since the Unix Epoch

UserId

string

ID of the user (GUID) Notes: We will send UserID only, no user name.

Webhook payload example when a user login:

{
"Action":"UserLoggedIn",
"Actor":{
"UserId":"19e62847-f302-4e32-9e4a-b6d8c304fcbe"
},
"PayloadId":"c02f0f73-1706-4973-9a4d-38007d6685e9",
"EventGroupName":"user",
"EventDatetimeUtc":1595204418000,
"UserId":"19e62847-f302-4e32-9e4a-b6d8c304fcbe",
}

Webhook for “Task”

Key

Type

Description

Action

string

The action that was performed. Can be one of:

Completed, Started, ReadyToWork, Bypassed, Reset, Assigned, Unassigned, Rejected, Approved

Actor

object

The user or the actor that triggered the event. Notes: We will send UserID only, no user name.

PayloadId

string

ID of the payload (GUID)

EventGroupName

string

The event group triggered. Value: task

EventDatetimeUtc

long

Unix Timestamp (milliseconds) - the number of milliseconds since the Unix Epoch

TaskId

string

ID of the task (GUID)

Assignee

object

The assignee of this task. Notes: We will send UserID only, no user name.

JobId

string

ID of the job (GUID)

JobCode

string

Job code

ClientId

string

ID of the client (GUID).

ClientName

string

Client name

ProductId

string

ID of the product (GUID)

ProductCode

string

Product code

StepId

int

ID of the step

StepName

string

Step name

StepStatusId

int

ID of the Step Status

StepStatusName

string

Step Status name

ShootingTypeId

int

ID of the Shooting type (aka Production type)

ShootingTypeName

string

Shooting type name (aka Production type name)

WorkflowId

string

ID of the Workflow

WorkflowName

string

Workflow name

StyleGuideId

string

ID of the Style guide

StyleGuideName

string

Style guide name

Webhook payload example when someone finishes a Photography task:

{
"Action":"completed",
"Actor":{
"UserId":"19e62847-f302-4e32-9e4a-b6d8c304fcbe"
},
"PayloadId":"c02f0f73-1706-4973-9a4d-38007d6685e9",
"EventGroupName":"task",
"EventDatetimeUtc":1595204418000,
"TaskId":"c02f0f73-4665-4aae-aac1-c2fbc65365de",
"Assignee":{
"UserId":"19e62847-f302-4e32-9e4a-b6d8c304fcbe"
},
"JobId":"d84eef10-c8a6-437a-9f95-0b58d2a3cb38",
"JobCode":"Summer 2020",
"ClientId":"2c856b2f-aded-4609-bf67-27009603b2b9",
"ClientName":"My Big client",
"ProductId":"7a782d57-b0bf-4743-8f02-2b1a1e229869",
"ProductCode":"AW2130146",
"StepId":"3",
"StepName":"Photography",
"StepStatusId":"9000",
"StepStatusName":"Done",
"ShootingTypeId":"1",
"ShootingTypeName":"On Model",
"WorkflowId":"2b5d3e1b-1706-4973-9a4d-57ab7d6685e9",
"WorkflowName":"My first workflow",
"StyleGuideId":"2376b9cb-67db-490f-a5e8-89811ed6b087",
"StyleGuideName":"Summer on the beach 2020"
}

Security

Validating the webhooks

The way to validate the webhook in your application is to hash the JSON payload of the webhook using the SHA256 algorithm and the secret key generated when you set up the webhook in gamma.

You will then compare the hash with the webhook signature. If they match, the webhook can be trusted.

The X-CF-Signature is 32 bytes and in hexa format. It's therefore important to make sure your hash function is returning 32-byte hexa as well.

Using a plain == operator is not advised. A method like secure_compare performs a "constant time" string comparison, which renders it safe from certain timing attacks against regular equality operators.

Below is an example of this process. The example is written in Ruby for a Sinatra server but should give you a good idea of the approach.

require 'sinatra'
require 'json'

post '/payload' do
request.body.rewind
payload_body = request.body.read

verify_signature(payload_body)

push = JSON.parse(payload_body)
puts "I got callback, JSON: #{push}"

return halt 200, "OK"
end

def verify_signature(payload_body)
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('SHA256'), ENV['SECRET_TOKEN'], payload_body)
return halt 500, "Signatures didn't match!" unless Rack::Utils.secure_compare(signature, request.env['HTTP_X_CF_SIGNATURE'])
end

Other

Content-Type

Creative Force always uses application/json as the content type.

Enable/Disable

You can choose to disable a webhook if you want to pause it temporarily.

Retry

The CF system will POST data to the payload URL specified and expects a 2xx HTTP status code return. If no 2xx HTTP status is returned, CF system will automatically retry (3 times only), each after 5 - 10 minutes.

Did this answer your question?