Configure Webhooks

What are webhooks

Webhooks are user-defined HTTP callback that notifies you about any action that has taken place.

YCloud uses webhooks to push real-time notifications to your applications. By configuring webhooks and subscribing to specific events, you can receive event notifications and use these notifications to execute actions in your backend systems.

Steps to receive webhooks

You can start receiving event notifications in your applications using the steps in this section:

  1. Identify the events to monitor and the event payloads to parse.
  2. Create a webhook endpoint.
  3. Handle requests from YCloud by parsing each event and returning 2xx response status codes.
  4. Secure your webhooks (recommended)

Step 1: Identify the events to monitor

Each event corresponds to a certain set of actions that can happen to your account. For example, if you subscribe to the sms.message.updated event you'll receive detailed payloads every time an SMS message is updated with a new status.

For a complete list of available webhook events and their payload examples, see Webhook Events & Payloads. See also Test webhooks for the detailed structured payload object.

Step 2: Create a webhook endpoint

You can use the Webhook Endpoints APIs, or go to Dashboard → Developers → Webhooks to manage webhooks.

Step 3: Handle requests from YCloud

Check event objects

Each event is structured as an event object with common properties: id, type, apiVersion, and createTime, and also contains properties unique to the event depending on type.

Here is an example of an event with type = whatsapp.message.updated:

{
  "id": "evt_eEVCy8eNqD9EvcFI",
  "type": "whatsapp.message.updated",
  "apiVersion": "v2",
  "createTime": "2023-02-22T12:00:00.000Z",
  "whatsappMessage": {
    "id": "63f5d602367ea403f8175a6c",
    "wamid": "wamid.BgNODYxN...",
    "status": "failed",
    "errorCode": "100",
    "errorMessage": "Parameter Invalid",
    "whatsappApiError": {
      "message": "(#100) Invalid parameter",
      "type": "OAuthException",
      "code": "100",
      "fbtrace_id": "AwmiSOCojlAkqvjCTjGt37r",
      "error_data": {
        "messaging_product": "whatsapp",
        "details": "Parameter Invalid"
      }
    },
    "totalPrice": 0.0,
    "currency": "USD",
    "bizType": "whatsapp"
  }
}

Return a 2xx response

Your endpoint must quickly return a successful HTTP status (e.g., 200) before any complex logic that could cause a timeout.

Build-in retries

If YCloud doesn't quickly receive a 2xx response for an event, we'll retry several times in the next few hours. Currently, we retry up to 7 times, with intervals of 10s, 30s, 5m, 30m, 1h, 2h, 2h.

Step 4: Secure your webhooks (recommended)

YCloud can sign the webhook events by including a signature in the HTTP header YCloud-Signature. This allows you to verify that the events were sent by YCloud, not by a third party.

The YCloud-Signature header contains a Unix Timestamp and a signature generated by HMAC with SHA-256.

YCloud-Signature: t=1654084800,s=8eb70f2acb056c2119acbee8fdd98a889021d9c268bc9ad248a4182c40e31119

Verify the signature by the following steps:

Step 1: Extract the timestamp and signatures from the header

Split the header, using the , character as the separator, to get a list of elements. Next, split each element, using the = character as the separator, to get a prefix and value pair.

The value for the prefix t corresponds to the timestamp and s corresponds to the signature.

Step 2: Prepare the signed payload string

The signed payload string is created by concatenating:

  • The timestamp (as a string)
  • The character .
  • The actual JSON payload (that is, the request body)

For example:

1654084800.{"id":"evt_eEVCy8eNqD9EvcFI","type":"whatsapp.message.updated","apiVersion":"v2","createTime":"2023-02-22T12:00:00.000Z","whatsappMessage":{"id":"63f5d602367ea403f8175a6c","wamid":"wamid.BgNODYxN...","status":"failed","errorCode":"100","errorMessage":"Parameter Invalid","whatsappApiError":{"message":"(#100) Invalid parameter","type":"OAuthException","code":"100","fbtrace_id":"AwmiSOCojlAkqvjCTjGt37r","error_data":{"messaging_product":"whatsapp","details":"Parameter Invalid"}},"totalPrice":0,"currency":"USD","bizType":"whatsapp"}}

Step3: Determine the expected signature

Before you can verify signatures, you need to retrieve your endpoint's secret by the Retrieve a webhook endpoint API.

Compute an HMAC with the SHA256 hash function. Use the endpoint’s signing secret as the key, and use the signed payload string as the message.

// Using library commons-codec (https://central.sonatype.com/artifact/commons-codec/commons-codec)
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;

public class MainClass {

  public static void main(String[] args) {
    String signedPayload = timestamp + "." + body;
    HmacUtils hmacUtils = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, secret);
    String expected = hmacUtils.hmacHex(signedPayload);
    System.out.println(expected);
  }
}
const crypto = require('crypto');

const toBeSignedPayload = timestamp + '.' + body
const expected = crypto.createHmac('sha256', secret)
.update(toBeSignedPayload)
.digest("hex");
using System;
using System.Security.Cryptography;
using System.Text;

class MainClass {
  public static void Main (string[] args) {
    string toBeSignedPayload = timestamp + "." + body;

    byte[] secretBytes = Encoding.UTF8.GetBytes(secret);
    byte[] messageBytes = Encoding.UTF8.GetBytes(toBeSignedPayload);
    HMACSHA256 cryptographer = new HMACSHA256(secretBytes);

    byte[] expectedBytes = cryptographer.ComputeHash(messageBytes);

    string expected = BitConverter.ToString(expectedBytes).Replace("-", "").ToLower();

    Console.WriteLine(expected);

  }
}
import json
import hmac
import hashlib

signed_payload = f'{timestamp}.{body}'
expected = hmac.new(
  key=secret.encode(),
  msg=signed_payload.encode(),
  digestmod=hashlib.sha256
).hexdigest()

print(expected)

Step 4: Compare the signatures

Compare the signature in the header to the expected signature. For an equality match, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance.