Webhooks

How to set up webhooks within Creative Force

Tejs Rasmussen avatar
Written by Tejs Rasmussen
Updated over a week ago

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 "Integration" and from there on the sub-menu "Webhooks":

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

  • Color Reference Created

  • Color Reference Updated

  • Color Reference Deleted

  • Move To Another Job

  • Bypass External Post

  • Change Post Vendor

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

  • Process without Color Reference

  • Failed

  • Copywriting Task Edit Made

WorkUnit Events

  • StatusChanged

  • Reset

  • Created

  • Deleted

  • Enabled

  • Disabled

  • ChangedSource

  • Completed

Job Events

  • Job Created

  • Job Deleted

  • Job Updated

  • Job Status Changed

  • Job Deadline Updated

Session Events

  • Add To Session

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, Completed, MoveToAnotherJob

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)

SampleCode

String

Sample Code

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",
"SampleCode": "88829384",
"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, Failed

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"
}

Webhook for Work Unit

Key

Type

Description

Action

string

The action that was performed. Can be one of:

StatusChanged, Reset, Created, Deleted, Enabled, Disabled, ChangedSource, Completed

Actor

object

The user or the actor that triggered the event. Notes: We will send UserID only, no user name. For some following cases when data is updated by background service, then Actor is empty:

* Sync from data source: create/delete product

PayloadId

string

ID of the payload (GUID)

EventGroupName

string

The event group triggered. Value: work unit

EventDatetimeUtc

long

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

ClientId

string

ID of the client (GUID).

ClientName

string

Client name

ProductId

string

ID of the product (GUID)

ProductCode

string

Product code

ProductionTypeId

int

Production Type ID

ProductionTypeName

string

Production Type Name

WorkUnitStatusId

int

Work Unit Status ID after changed

WorkUnitStatusName

string

Work Unit Status Name after changed

Webhook payload example when a work unit status changed:

{
"Action":"StatusChanged",
"Actor":{
"UserId":"19e62847-f302-4e32-9e4a-b6d8c304fcbe"
},
"PayloadId":"c02f0f73-1706-4973-9a4d-38007d6685e9",
"EventGroupName":"work unit",
"EventDatetimeUtc":1595204418000,
"ClientId":"2c856b2f-aded-4609-bf67-27009603b2b9",
"ClientName":"My Big client",
"ProductId":"7a782d57-b0bf-4743-8f02-2b1a1e229869",
"ProductCode":"AW2130146"
"ProductionTypeId":1,
"ProductionTypeName":"OnModel"
"WorkUnitStatusId":3000
"WorkUnitStatusName":"InProgress"
}

Webhooks for Jobs

Key

Type

Description

Action

string

The action that was performed. Can be one of:

Created, Updated, Deleted, StatusChanged, DeadlineUpdated

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: Job

EventDatetimeUtc

long

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

JobId

string

ID of the job (GUID)

JobName

string

Job name

JobCode

string

Job code

JobStatusId

int

Job Unit Status ID

JobStatusName

string

Job Unit Status Name

ClientId

string

ID of the client (GUID)

ClientName

string

Client Name

JobDeadlineUtc

long

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

Webhook payload example when someone deletes a Job:

{
"Action":"Deleted",
"Actor":{
"UserId":"19e62847-f302-4e32-9e4a-b6d8c304fcbe"
},
"PayloadId":"c02f0f73-1706-4973-9a4d-38007d6685e9",
"EventGroupName":"Job",
"EventDatetimeUtc":1595204418000,
"JobId":"d84eef10-c8a6-437a-9f95-0b58d2a3cb38",
“JobName:”Summer 2020”,
"JobCode":"Summer 2020”,
“JobStatusId”:3000,
“JobStatusName”:”In Progress”,
"ClientId":"2c856b2f-aded-4609-bf67-27009603b2b9",
"ClientName":"My Big client",
“JobDeadlineUtc”:1595204529000,
}

Webhook for Session

Key

Type

Description

Action

string

The action that was performed. Can be one of:

AddedToSession

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: session

EventDatetimeUtc

long

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

ProductId

string

ID of the product (GUID)

ProductCode

string

Product code

ShootingTypeId

int

ID of the Shooting type (aka Production type)

ShootingTypeName

string

Shooting type name (aka Production type name)

WorkUnitId

string

ID of the WorkUnit (GUID)

SessionId

string

ID of the Session (GUID)

SessionCode

string

Session Code

SessionName

string

Session Name

PlannedTimeSlot

JSON literal

Time of Session

PlannedTeam

JSON literal

PlannedTeam consists of individuals participating in that session and specifies who they are and their respective roles.

PlannedProducerId

string

ID of the Planned Producer (GUID)

PlannedProducerName

string

Planned Producer Name

PlannedSetId

string

ID of the Planned Set (GUID)

ProductionItemId

string

ID of the Production Item (GUID)

PlanningStatus

JSON literal

Status (ID, Name) of the planning

Can be one of: Unplanned = 2, Planned = 1

ActionSource

string

The app that triggered the event

Webhook payload example when someone Add a Session:

{
"ActionSource": "AddFromGateway",
"SessionId": "cba7640d-452b-417b-8bbc-aaa92a763359",
"SessionCode": "SES113736CF",
"SessionName": "Quyen Test Add session",
"PlannedTimeSlot": "[{\"PlannedStart\":\"2023-12-10T01:00:00\",\"PlannedEnd\":\"2023-12-10T10:00:00\"}]",
"PlannedTeam": "{\"Assistant\":\"phuonglth+dev@creativeforce.io,dungnt+sing@creativeforce.io\",\"Photographer\":\"hanh@creativeforce.io\",\"HairAndMakeup\":\"duongvtt+dev@creativeforce.io\",\"DigitalProcessing\":\"hanttn@creativeforce.io\"}",
"PlannedProducerId": "429f37e8-4583-4541-89d9-49230bbf046f",
"PlannedProducerName": "DuongVTT DEV",
"PlannedSetId": "53c4bc2d-244a-490e-9f75-e3554246b6dd",
"ProductionItemId": "1e6b01e0-671e-462c-b266-692d6a16eb92",
"PlanningStatus": "{\"PlanningStatusId\":2,\"PlanningStatus\":\"Unplanned\"}",
"WorkUnitId": "1e6b01e0-671e-462c-b266-692d6a16eb92",
"ShootingTypeId": 1,
"ProductId": "99ecc381-41f1-4782-bcd4-e762e50a8a93",
"ProductCode": "Product_230831_5",
"PayloadId": "d483e7cb-e03b-4ccd-b307-22ca3e990e96",
"EventGroupName": "session",
"EventDatetimeUtc": 1701935735820,
"Action": "AddedToSession",
"Actor": {
"UserId": "600a5a14-8bd5-4d48-888e-f2b0bf2defc8"
}
}

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?