You can use Webhooks to subscribe to certain events when they occur. Webhooks provide a way to respond to real-time events as they happen without having to constantly poll the API. When an event occurs that matches your webhook scope, a POST HTTP request will be sent to your webhook URL.

Setup

To quickly get started testing webhooks, you can use something like RequestBin to inspect and explore the payload.

Check out the Core API Reference on how to create a webhook.

Retries

The POST HTTP request to your URL expects an HTTP 200 response code. Any other response code that is not exactly a 200 will result in the Webhook to retry with an exponential backoff strategy with a max of 10 attempts. A webhook with a total error count above 25 will be disabled. Total error count is reset with each successful response.

Scopes

ScopeDescription
*Matches all events.
project.*Matches all project events.
project.createdFires when a project is created.
project.updatedFires when a project is updated.
project.label_addedFires when a label is added to a project
project.contact_createdFires when a contact is added to a project
project.contact_updatedFires when any edits are made to a project's contact
project.mergedFires when a project is merged with another project
project.deletedFires when a project is deleted.
photo.*Matches all photo events.
photo.createdFires when a photo is created and processed by our system.
photo.updatedFires when any edits (annotations) are made to a photo.
photo.tag_addedFires when a tag is added to a photo
comment.*Matches all comment events.
comment.createdFires when a new comment is created.
document.*Matches all document events
document.createdFires when a document is created for a project
video.*Matches all video events
video.createdFires when a video is created for a project
todo_list.*Matches all todo_list (checklist) events
todo_list.createdFires when a todo list (checklist) is created
todo_list.completedFires when a todo list (checklist) is completed
todo_list.deletedFires when a todo list (checklist) is deleted
task.*Matches all checklist task events
task.completedFires when a checklist task is completed. Note: This does not apply to Project Tasks

Example Request Body

{
  "event_type": "project.created" - (String) This will correspond to the event and scope that triggered the webhook.
  "created_at": 1661361759 - (Integer) This is the integer timestamp when the webhook request was sent.
  "payload": {...} - (Object) This is the object that corresponds to the event that occurred, and will typically match the structure of the associated object.  e.g. "project.*" webhook payloads will match the Project object.
  "webhook_id": 42 - (Integer) This is the id of the webhook object that the webhook request came from.
}

Validating your payload

Because your webhook URL is open to the public, for security reasons, we recommend validating that the request came from CompanyCam by using the token parameter you supplied calling the create webhook endpoint with the X-CompanyCam-Signature header from the event.

To validate that the data came from CompanyCam, you need to calculate the base64 encoded HMAC hash of the request body using the SHA1 algorithm and your webhook token as the key. If the value matches the header's signature, you can be sure the request was sent from CompanyCam.

const crypto = require('crypto');
const webhookToken = 'YOUR_WEBHOOK_TOKEN';

function validateFrontSignature(data, signature) {
    var hash = crypto.createHmac('sha1', webhookToken)
                     .update(data)
                     .digest('base64');

   return crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(signature));
}

// Get response...

const receivedData = res.body; // raw body of the request
const isValid = validateFrontSignature(receivedData, receivedSignature);