Application webhooks

Overview

Application webhooks run alongside Front apps. To learn the differences between application webhooks and rule webhooks, refer to our Webhooks overview.

Setup

Application webhooks are configured as features on the apps you create on the Developers page in Front. Refer to the Create, manage, and publish apps topic to learn how to create application webhooks.

πŸ“˜

OAuth is required for published apps

If your webhook is part of an app that you are publishing on our App Store, you must add OAuth to the app so that customers can authorize that Front events be sent to your webhook from their instance.

Application webhook validation

In order to configure application webhooks in the Developer Settings as described in the Setup section, you need to have a server running which is able to respond to requests. When you attempt to save your webhook configuration (both on initial creation and on future updates), we will send the following notification to your webhook URL.

Header:

{
  'x-front-signature': <built from your application signing key>,
  'x-front-request-timestamp': <timestamp in milliseconds>,
  `x-front-challenge`: <random string>
}

Body:

{
  type: 'sync',
  authorization: {
    id: "cmp_abc" // company id you would see from /me
  }
}

Verifying integrity

These steps will allow you to verify that the incoming application webhook originated from Front:

  1. Concatenate the stringified timestamp from the x-front-request-timestamp header with a colon
  2. Convert the string from step 1 to a buffer
  3. Take the buffer from step 2 and concat it with the raw request body
    1. IMPORTANT: always operate on the raw request body to ensure signature stability
  4. Convert the concatenated buffer from step 3 to a string.
  5. Apply the hmac SHA256 algorithm using the token provided during webhook configuration as the key [output in base 64].
  6. Compare the result from step 5 to the string provided in the x-front-signature header.

πŸ“˜

Make sure to encode the payload using UTF-8.

Example implementations:

const baseString = Buffer.concat([Buffer.from(`${timestamp}:`, 'utf8'), buf]).toString();
const hmac = crypto.createHmac('sha256', token)
        .update(baseString)
        .digest('base64');
const fromFront = (hmac === signature);
from flask import Flask, request, abort
import hashlib
import hmac
from base64 import b64encode

app = Flask(__name__)

@app.route('/appwebhook', methods=['POST'])
def handle_webhook():
    
    application_secret = 'YOUR_APPLICATION_SECRET_HERE'
    raw_body = request.data
    signature = request.headers.get("x-front-signature")
    x_front_challenge = request.headers.get('x-front-challenge')
    
    #Steps 1-5
    timestamp = request.headers.get("x-front-request-timestamp") + ":"
    buffer = timestamp.encode()
    concatenated = buffer + raw_body
    hashed = b64encode(hmac.new(application_secret.encode(), concatenated, hashlib.sha256).digest()).decode()
    
    #Step 6 and responding to the validation request
    if hashed == signature:
        return x_front_challenge, 200, {'Content-Type': 'text/plain'}
    else:
        abort(400, "Bad Request: validation failed")
       
if __name__ == '__main__':
    app.run(port=5000) 

Responding to the validation request

To successfully validate the initial validation request, your application webhook must reply:

  • Within 10s
  • With a status code 200
  • With one of the following content-type/body pairs:
    1. content-type=text/plain with the body being the value of x-front-challenge
    2. content-type=application/x-www-form-urlencoded with a challenge parameter: challenge=<x-front-challenge-value>
    3. content-type=application/json with the body: {"challenge": "<x-front-challenge-value>"}

If the response doesn’t meet the criteria above, the validation is considered failed. You cannot save your change to your webhookUrl if the validation fails.

Handling webhook events

With each event received during normal operation, the request headers will include the following HTTP request headers which you can use to verify the integrity of the request;

'x-front-signature': <built from your application signing key>
'x-front-request-timestamp': <timestamp in milliseconds>

You should respond to each received webhook event with an HTTP status code and JSON message.

When acknowledging successful receipt of a webhook event, you should send an HTTP 200 response with the JSON message { "type": "success" }.

If you need Front to back off, send HTTP 429 with a { "type": "too_many_requests" } JSON message, and the request will be retired up to three times.

An HTTP 500 error with JSON message {"type": "internal_error"} will also cause Front to retry the request up to three times.

Differentiating customer webhook requests

Application webhooks include an authorization object on the payload that allows you to determine the customer instance emitting the webhook. The ID of the customer is displayed in authorization.id. By recording this ID, you can map a Front company ID to your own customer ID. Every subsequent payload receive will continue to provide the authorization.id. All published webhooks require that your application define an OAuth client, which is where the customer ID is injected from.

πŸ‘

If you have an API token, you can request the ID of the customer using the API Token details endpoint.

Timeouts

Webhook requests issued by Front will time out after 5 seconds.

Retries

Application webhooks will retry up to 3 times when Front receives an error or otherwise unrecognized response from your server. If you suspect you missed data by sending a bad response and also missing the retry opportunities, consider polling the List events API endpoint to filter for Events which occurred during the time you missed data.

πŸ“˜

If your webhook fails to respond successfully, Front will disable it

If we fail to receive a successful response after the last retry, Front will disable your webhook and stop sending events. To re-enable the webhook, go to the Webhook feature in your developer app and click Update to re-verify and re-enable it.