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

  • Reset
  • Deleted
  • StatusChanged
  • StyleguideChanged
  • CategoryChanged
  • Ignored
  • ColorReferenceMigrated
  • PropertiesUpdated

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

Webhook for “Product”

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”

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”

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”

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.

Did this answer your question?