All of our webhook calls contain a signature that you can use to verify the validity of the event contents.

Our POST request to your subscription URL will contain an HTTP header called X-JWT-SIGNATURE with a jwt token signed by us using the RS512 algorithm.

The JWT has the following structure:

{
  "iss": "[SIGNING_KEY_ID]",
  "iat": iat,
  "digest": "[REQUEST_BODY_DIGEST]"
}
  • SIGNING_KEY_ID can be either lirium-sandbox or lirium-production depending on the environment where the webhook call was sent from.
  • iat is the time when the JWT was generated, it's an int representing the seconds from POSIX time. You can use this value to verify that the JWT is recently generated.
  • REQUEST_BODY_DIGEST is the hex representation of the sha256 digest of the request body bytes.

The following python code example can be used to validate the signature of a request received using flask from our sandbox environment:

from jose import jwt
from hashlib import sha256
import flask

token = flask.request.headers.get("X-JWT-SIGNATURE")
decoded = jwt.decode(token, signing_public_key, "RS512")

digest = sha256(flask.request.data).hexdigest()

assert decoded.get("digest") == digest
assert decoded.get("iss") == "lirium-sandbox"

The corresponding public keys for each of our environments can be found here: Webhooks Public Keys

As an example, this JWT

{
  'iss': 'lirium-sandbox', 
  'iat': 1646758802, 
  'digest': '075b83598e4e147f240f5b15694b42063741b677548c89c9a4f100b5a36263f5'
}

Would yield the following JWT Signature

eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJsaXJpdW0tc2FuZGJveCIsImlhdCI6MTY0Njc1ODgwMiwiZGlnZXN0IjoiMDc1YjgzNTk4ZTRlMTQ3ZjI0MGY1YjE1Njk0YjQyMDYzNzQxYjY3NzU0OGM4OWM5YTRmMTAwYjVhMzYyNjNmNSJ9.ONcsg4ZWPKVuXC_nGWk7zfKuoqWgfV4edxDeweL7S4cSkj9SdnxZnxSBdPFMzeIaPAETnFNc-6Pxcd919pYPIx0M155yEBP4SHwmsflh93vrjdoUZYB2sD3XL6xeiCQ0KuwRp_yHpICO9JzCto4XsbvZ_wmhI1wIl-hDnlPDfdbL--rV-usbx1EPXoS67v3oTV2YtdvtoO5QZAy7relxyA9hLEpsKv_Duf_RYDiNkGEl_aZrKu8fQEFf0CUZkbqsbv1Xt-VBavk1A3muGXJfOHjBxbT5er0_Mkn7pj5T1o4XiRLlz-WhYjwsKaewpvsdoFVt_ggFPOwcBYaOLKPx5Xfapt7WtsxSH3gds9PzUGj9mi4lIm-wCYjIn7tb9iimwfhkNm_6himIflLGCQ1eu3n1UzirnX5OvlmBOutXkHddWxzp1xBn5PuWWRMMHGWg-tZcUl5x4cV9kkkse9FmheHkstjf0Ham3ePCydlMp5r0vjrgsXbHaC3iLSf_3al7wpkLXogWiVVbqAkoLYpy3nCTZ_i0btnn5GwcRW2Gx5-ILNoxLe49lMD_le-iSEb9VaC7n8lyZYw3WTXdN1a0RAAO3RbkT8UahqTxfH_iqWbbRcr1Uep92OKTt3xbaCqvyGMOSLSO-nlnXrLgyZHllG0K59wUwQjp2GcMVOljoNU