Overview
Integration
Mainly, there are 2 ways to integrate with McEasy’s webhook service.
Manual Request
This method is done via a request to McEasy’s customer service/developer. The required data for this method are:
- Company name/domain.
- Webhook name (eg.: “Geofence Alert Webhook”).
- URL (Company’s/Customer’s own endpoint).
- The valid URL format for registration is the typical URL containing scheme, domain name, path (if desired), and query if it needs to have them. And should look like something like these.
https://www.notify.co.uk/event
http://listen.com/event1
http://wh-event.company.co.it:3000/154
https://www.listeners.co.us/listen?token=eyAsdaksjbcaJJBashsfnvgpVs
- The valid URL format for registration is the typical URL containing scheme, domain name, path (if desired), and query if it needs to have them. And should look like something like these.
- Events to be listened to. The list of listenable events can be found here.
- (Optional) If signature verification is desired, a webhook secret.
API (beta)
This method is the traditional method for interacting with McEasy’s webhook service. The customer would use the pre-existing endpoints to do CRUD operations on their URLs. For more information, check the API documentation.
Sent Data
Header
In each sent data, a header will be included.
It contains the metadata of the events and also serves as a way for clients to verify data signature.
Content-Type will always send application/json
.
X-McEasyWebhook-Signature
will not be included in the header if the client does not specify a webhook secret.
The following is the data that will be sent in the header:
NOTE
Please note that the name could be converted to all lowercase automatically depending on which http library is used, for example JavaScript’s Express Request.header() function converts header names to all lowercase (still retaining the dash symbol
-
). The header name conversion does not matter, only the values matter.
Header name | Value |
---|---|
User-Agent | McEasyWebhookService/<current_version> |
Content-Type | application/json |
X-McEasyWebhook-Timestamp | <UNIX timestamp in millisecond of the time the event was sent> |
X-McEasyWebhook-Event | <Event id of the event> |
X-McEasyWebhook-Occurrence-ID | <Job id of the process handling the event> |
X-McEasyWebhook-Signature | <Signature used for verification> |
Body
Data will be sent as a JSON object. The body containing the data will have a main structure and the event data structure.
The main structure contains some meta information of the event that may help developers to determine which event is sent,
while the event
data structure contains all the details of the event.
Also, data
in the data
field will have different structure depending on what event type it is.
For more details about the data
field's structure, refer to the Events section. The structure is as follows:
{
event_time: int,
event_id: int,
event_name: string,
event_type: string,
event: {
data: {
// Data here
}
}
}
Security
Security for Webhook requests consists of verifying signature sent with every event. The signature will be stored under X-McEasyWebhook-Signature (Refer to Header for more information).
The signature is a combination of (in order) system’s time, event id, data in raw JSON format, separated with this character combination |>
and hashed using HMAC technique with SHA256 algorithm, which is later encoded into base64 and prefixed with v1.0:
(v
+ <version_number>
+ :
).
The secret used in the HMAC is acquired from the registered webhook secret of each company and should be decoded from base64 before being used in the HMAC, since it is encoded into base64 when storing in the database.
Pre-encoded secret:
Hi this is supposed to be a secret!
Encoded secret:
SGkgdGhpcyBpcyBzdXBwb3NlZCB0byBiZSBhIHNlY3JldCE=
Pre-hashed signature:
Format:
<system_time> + |> + <event_id> + |> + <json_data>
.
41425525|>6|>{\n“data”:”example”\n}
Full signature:
v1.2:fasdGgagsvblaFASfawkfiasSvnmaVVms4==
Example code for signature validation implementation (TypeScript language):
function validateSignature(
headerSignature: string,
req: Request // Express's Request class
): boolean {
const signature = req.header("x-mceasywebhook-signature"),
time = req.header("x-mceasywebhook-timestamp"),
eventId = req.header("x-mceasywebhook-event"),
const secret = "TWNFYXN5SGFzQVNlY3JldA==";
const decodedSecret = Buffer.from(secret, "base64").toString("binary");
const hmac = createHmac("sha256", decodedSecret);
hmac.update(`${time}|>${eventId}|>${data}`);
const base64EncodedSignature = hmac.digest("base64");
const fullSignature = `v1.0:${base64EncodedSignature}`;
return headerSignature == fullSignature;
}