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:
- Concatenate the stringified timestamp from the
x-front-request-timestamp
header with a colon - Convert the string from step 1 to a buffer
- Take the buffer from step 2 and concat it with the raw request body
- IMPORTANT: always operate on the raw request body to ensure signature stability
- Convert the concatenated buffer from step 3 to a string.
- Apply the
hmac
SHA256 algorithm using thetoken
provided during webhook configuration as the key [output in base 64]. - 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:
content-type=text/plain
with the body being the value ofx-front-challenge
content-type=application/x-www-form-urlencoded
with a challenge parameter:challenge=<x-front-challenge-value>
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.
Payload
The webhook payload will be delivered as a JSON object. The structure you should expect to receive is the following;
{
type: 'inbound_received', // type of event being processed. See "Event types" below
authorization: {
id: "cmp_abc" // company id you would see from /me
},
payload: { ... } // the event object. See "Payload object" below
}
Event types
The event type
field will be one of the following.
Event type | JSON type value |
---|---|
Inbound messages | inbound_received |
Conversation moved | conversation_moved |
Outbound messages | outbound_sent |
Message delivery failed | message_delivery_failed |
Conversation archived | conversation_archived |
Conversation reopened | conversation_reopened |
Conversation deleted | conversation_deleted |
Conversation restored | conversation_restored |
Conversation snoozed | conversation_snoozed |
Snooze expired | conversation_snooze_expired |
Comment added | new_comment_added |
Assignee changed | assignee_changed |
Tag added | tag_added |
Tag removed | tag_removed |
Link added | link_added |
Link removed | link_removed |
Payload object
The value of the payload
object will be a JSON event object. The structure of these events can change based on the type of event being received. You can take a look at this Example Event Object for an idea of what these events should look like.
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.
Updated about 1 month ago