Webhooks

Lynx uses webhooks to send notifications to the client application when an event of "interest" occurs. A number of Lynx API calls (such as enrollment API) require additional time for the processing to complete. In such cases, the API being invoked returns a response immediately with a success code - however Lynx will notify the client application using a pre-registered webhook once the entire process completes.

❗️

New event types are continuously being added to the platform. Clients have the option of subscribing to events that are of interest. It is up to you (the client) to decide which events you want to process and how.

Create Webhook API

Webhooks can be created using the Lynx Client Dashboard (under the Settings menu) or via API call.

PropertyValue
MethodPOST
URLhttps://dev.lynx-fh.co/clients/webhooks
DescriptionAdds a configuration webhook URL for a client organization
AuthBearer JWT

Example Request

{
  "data": {
    "clientOrg": {
      "name": "Health_Plan_ABC_Division"
    },
    "clientOrgWebHook": {
      "url": "https://your-app.com/webhooks/lynx",
      "encryptionIndicator": true,
      "deliveryMode": "AtLeastOnce"
    }
  },
  "idempotency": {
    "idempotencyKey": "cf82639c-e3b6-4d51-9871-0e452a2b1566"
  }
}
FieldTypeDescription
clientOrg.namestringThe name of your client organization
clientOrgWebHook.urlstringThe endpoint URL where Lynx will send webhook event notifications
encryptionIndicatorbooleanSet to true to encrypt the webhook payload using AES-256-GCM. A secret key and IV will be provided
deliveryModestringAtLeastOnce (retries on failure) or AtMostOnce (single delivery attempt)
idempotencyKeystringA unique identifier (UUID) to prevent duplicate webhook registrations

Payload Encryption

Webhooks can be configured to encrypt the message payload. A secret key and an initialization vector are provided as part of the response when a webhook is created.

The encryption is based on the Advanced Encryption Standard (AES-256) - a symmetric-key algorithm. Below are code snippets on how to decrypt an encrypted payload, based on the secret key and initialization vector:

public static String decrypt(String secretKey, String initializationVector, String cipherText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
  // Initialize GCM Parameters
  // Same base64EncodeKey, IV and GCM Specs are to be used for encryption and decryption.
  GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_BIT_LENGTH, Base64.getDecoder().decode(initializationVector));
  Cipher decryptCypher = Cipher.getInstance(AES);
  decryptCypher.init(Cipher.DECRYPT_MODE, convertStringToSecretKey(secretKey), gcmParameterSpec, drbgSecureRandom());
  byte[] plainTextArray = null;

  plainTextArray = decryptCypher.doFinal(Base64.getDecoder().decode(cipherText));

  return new String(plainTextArray);
}

/**
  * Create a SecretKey object from a given base64 secretKey string
  * @param secretKey inbound secret key as string encoded in base64
  * @return the SecretKey type object
  */
public static SecretKey convertStringToSecretKey(String secretKey) {
  byte[] decodedKey = Base64.getDecoder().decode(secretKey);
  return new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
}
// NPM module required to decrypt (npm install crypto)
const crypto = require('crypto');

/**
 * The decrypt function decrypts the inbound encrypted message. All inbound 
 * parameters are base64 encoded.
 */
const decrypt = function(encryptedMsg, secret, iv) {
  // decode base64 message
  const bufferData = Buffer.from(encryptedMsg, 'base64');
  // separate message body and tag from message
  let msg = bufferData.slice(0, bufferData.length - 16);
  let authTag = bufferData.slice(bufferData.length - 16, bufferData.length);

  // decode base64 secret and init vector
  let decodedSecret = Buffer.from(secret, 'base64');
  let decodedIV = Buffer.from(iv, 'base64');

  // decrypt message body
  let decipher = crypto.createDecipheriv('aes-256-gcm', decodedSecret, decodedIV);
  decipher.setAuthTag(authTag);
  let decipheredText = decipher.update(msg, 'utf8');
  decipheredText += decipher.final('utf8');
  console.log(decipheredText)
};

Delivery Mode

Lynx supports two methods of delivery attempts for the webhook subscriptions:

Delivery ModeDefinition
Single (at most once)An attempt will be made to deliver a single webhook event to the specified URL. If the delivery fails for any reason (timeout or HTTP response code other than 200), the event will not be retried.
At least onceAn attempt will be made to deliver a webhook event to the specified URL. If the delivery fails for any reason (timeout or HTTP response code other than 200), the event will be retried multiple times (every 5 minutes) until an HTTP response code of 200 is received.

Steps to Receive Webhooks

Steps to Receive Webhooks
You can start receiving event notifications in your app using the steps in this section

  • Identify the events you want to monitor and the event payloads to parse.
  • Configure a Lynx client webhook endpoint as an HTTP endpoint (URL) on your local server.
  • Handle requests from Lynx by parsing each event object and returning 2xx response status codes.

Webhooks can be tested locally via https://ngrok.com or remotely via https://webook.site. Both provide unique URLs to use for your webhook and capture incoming requests for examination.