Overview

Webhooks allow you to build or setup integrations with Teamwork products by subscribing to certain events. These events could be triggered by the actions of anyone in the organization. When one of these events is triggered, we’ll send a HTTP POST request to an URL you provide. Webhooks could be used to build integrations, generate reports, back up data, and more.

Creating a webhook

You can create a webhook through the web interface by going to your Settings and clicking Webhooks in the sidebar. Or you could create a webhook through the API, see Webhooks API documentation.

Events

When configuring a webhook you’ll be able to choose which events you’d like to be notified for. By subscribing to only the events in which you’re interested in you can reduce the aount of HTTP requests to your server. You can add or remove events from your webhook without recreating it via the UI or API.

Below is a list of the available events:

Name Cause
activities.created Activity created
activities.deleted Activity deleted
activities.updated Activity updated
companies.created Company created
companies.deleted Company deleted
companies.updated Company updated
contacts.created Contact created
contacts.deleted Contact deleted
contacts.updated Contact updated
currencies.created Currency created
currencies.deleted Currency deleted
currencies.updated Currency Updated
custom-fields.created Custom field created
custom-fields.deleted Custom field deleted
custom-fields.updated Custom field updated
custom-filter.created Custom filter created
custom-filter.deleted Custom filter deleted
custom-filter.updated Custom filter updated
deals.created Deal created
deals.deleted Deal deleted
deals.updated Deal updated
files.created File created
files.deleted File deleted
files.updated File updated
installation-settings.updated Installation settings updated
lost-reasons.created Lost reason created
lost-reasons.deleted Lost reason deleted
lost-reasons.updated Lost reason updated
notes.created Note created
notes.deleted Note deleted
notes.updated Note updated
pipelines.created Pipeline created
pipelines.deleted Pipeline deleted
pipelines.updated Pipeline updated
products.created Product created
products.deleted Producted deleted
products.updated Product updated
stages.created Stage created
stages.deleted Stage deleted
stages.updated Stage updated
users.created User created
users.deleted User deleted
users.updated User updated

Request body

Unless stated otherwise the request body will contain the same JSON model as described in our API documentations for the type. For example, deals.created will contain the deal model.

Receiving and handling

Configuring your server to receive a new webhook is no different from creating any page on your website. With PHP, you might create a new .php file on your server; with a framework like Sinatra, you would add a new route with the desired URL. Remember, with webhooks, your server is the server receiving the request.

When an event occurs in your Teamwork CRM account a HTTP POST request is sent to the registered URL.

Delivery headers

Header Description
Event The event name of the webhook e.g deals.created
Signature The signature generated by Teamwork using your secret token so you can determine if the request is genuine
Delivery The delivery UUID, this is listed in the UI for you to cross reference in your webhook settings
User-Agent The version of the webhook service used to make this request

Verifying the request

You can optionally provide a token when creating the webhook, this token will be used as the key in the HAMC-SHA256 hex encoded signature in the Signature header. Code samples are given below as an example of how to verify this signature by creating your own and comparing it.

Responding to the request

Responses with a status code greater than 400 will be considered a failure, failures will be reattempted 3 times before permanent failure.

Reattempts are made on the following schedule:

Reattempt # Delay (mins)
1 1
2 5
3 10

Code samples

Go

package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

const secretToken = "bea1ffc4e56d5056b6e0c7ee24b47b5"

// some error handling omitted for brevity
func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			http.Error(w, "", http.StatusBadRequest)
			return
		}

		b, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "", http.StatusInternalServerError)
			return
		}

		if !validSignature(r.Header.Get("Signature"), b) {
			http.Error(w, "", http.StatusBadRequest)
			return
		}

		fmt.Println("Verified Teamwork Webhook:")
		for h, v := range r.Header {
			fmt.Printf("%s: %s\n", h, v[0])
		}
		fmt.Println("-------------------")
		fmt.Println(string(b))
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}

func validSignature(sig string, body []byte) bool {
	mac := hmac.New(sha256.New, []byte(secretToken))
	mac.Write(body)
	return sig == hex.EncodeToString(mac.Sum(nil))
}

PHP

<?php
define('SECERT_TOKEN', 'bea1ffc4e56d5056b6e0c7ee24b47b5');
$body = file_get_contents('php://input');

$sig = hash_hmac('sha256', $body, SECERT_TOKEN, false);
if ($sig !== $_SERVER['SIGNATURE']) {
  error_log('Bad signature');
  http_response_code( 400);
  exit;
}

error_log('Verified Teamwork Desk Webhook Event: '.$_SERVER['EVENT']);

error_log($body);

Feedback

If you have any feedback or suggestions, feel free to contact us at api@teamwork.com.