Guide on how to implement Lasso webhooks for real-time updates
Webhooks allow you to receive real-time notifications about actions taken on the Lasso Moderation platform. By configuring a webhook URL, Lasso Moderation can automatically send a JSON payload to your server whenever a particular event occurs on the platform. Webhooks can be configured under settings in the dashboard.
Webhook
To set up a webhook, you need to provide a valid URL that can receive HTTPS POST requests. When an event is triggered on the platform, Lasso Moderation will send a JSON payload to the specified URL. This payload contains information about the event that occurred, including any relevant data that you may need to take action.
Multiple actions can be sent at once in one HTTP POST request, this is done in case of bulk operation where multiple actions are taken at once. The actions are ordered by when they are taken.
Here are example responses from a webhook that is triggered based on different actions:
{"actions": [ {"action_type":"ChangeStatus","action_id":"clf10kbhp0012sauvpxlqsb6h",// The Lasso ID of the action"action_created_at":"2023-03-11T15:02:13.178Z",// ISO date of when the action was taken"type":"user",// The type of object the action is taken on, "user", "content" or "subcategory""status":"hidden",// The status of the object, this can be "allowed", "flagged" or "hidden" "previous_status": "flagged", // The status of the object before the action was taken, this can be `null` (when there was not previous status), "allowed", "flagged" or "hidden"
"actor_id": "cldk3z9ze0004saiy542wfbck", // (optional) The Lasso ID of the moderator who triggered this action. Either actor_id or rule_id is set.
"rule_id": "clyhppfjy00574ohv9uigmnia", // (optional) The Lasso ID of the rule which triggered this action. Either actor_id or rule_id is set.
// (optional) user is only sent when type === "user""user": {"id":"cldk3z9ze0004saiy542wfbck",// The product's ID of the user"tags": ["potential-spammer"] // Custom tags attached to the user },// (optional) content is only sent when type === "content""content": {"id":"cldk3zadj019wsaiyudwdtxtr",// The product's ID of the content"created_at":"2023-03-11T15:02:13.178Z",// ISO date of when the content was created"user_id":"cldk3z9ze0004saiy542wfbck",// The ID of the user who sent the content"subcategory_id":"cldvl1z2l002xsaykyoje94od",// The ID of the subcategory the content is part of"category_id":"cldq51mh005fjsaw2y0fx3t5c"// The ID of the category the content is part of"tags": ["explicit-content"] // Custom tags attached to the content },// (optional) subcategory is only sent when type === "subcategory""subcategory": {"id":"cldvl1z2l002xsaykyoje94od"// The product's ID of the subcategory },// (optional) when the user is temporarily banned"temporary_ban": {"until":"2023-12-18T17:42:38.558Z","period":"week","duration":1 },"policy_id":"inappropriate-content",// (optional) The ID of the policy that was used when taking this action "policy_name": "Inappropriate Content", // (optional) The name of the policy that was used when taking this action
"policy_information": "This content contains XXX" // (optional) Extra information that was given when the action was taken
} ]}
{"actions": [ {"action_type":"AddTags",// Or "RemoveTags""action_id":"clf10kbhp0012sauvpxlqsb6h",// The Lasso ID of the action"action_created_at":"2023-03-11T15:02:13.178Z",// ISO date of when the action was taken"type":"user",// The type of object the tag was added to, "user" or "content""tags_added": ["tags-slug1","tag-slug2"],// (optional, only when action type is "AddTags""tags_removed": ["tags-slug1","tag-slug2"],// (optional, only when action type is "RemoveTags" "actor_id": "cldk3z9ze0004saiy542wfbck", // (optional) The Lasso ID of the moderator who triggered this action. Either actor_id or rule_id is set.
"rule_id": "clyhppfjy00574ohv9uigmnia", // (optional) The Lasso ID of the rule which triggered this action. Either actor_id or rule_id is set.
// (optional) user is only sent when type === "user""user": {"id":"cldk3z9ze0004saiy542wfbck",// The product's ID of the user"tags": ["potential-spammer"] // Custom tags attached to the user },// (optional) content is only sent when type === "content""content": {"id":"cldk3zadj019wsaiyudwdtxtr",// The product's ID of the content"created_at":"2023-03-11T15:02:13.178Z",// ISO date of when the content was created"user_id":"cldk3z9ze0004saiy542wfbck",// The ID of the user who sent the content"subcategory_id":"cldvl1z2l002xsaykyoje94od",// The ID of the subcategory the content is part of"category_id":"cldq51mh005fjsaw2y0fx3t5c"// The ID of the category the content is part of"tags": ["explicit-content"] // Custom tags attached to the content } } ]}
{"actions": [ {"action_type":"UpdateList","action_id":"clf10kbhp0012sauvpxlqsb6h",// The Lasso ID of the action"action_created_at":"2023-03-11T15:02:13.178Z",// ISO date of when the action was taken"actor_id":"cldk3z9ze0004saiy542wfbck","list": {"id":"cln4sal9000d6saf8m5g12ifp","name":"banned words",// (optionally in case items are added to the list)"words_added": ["F you!" ],// (optionally in case items are removed from list)"words_added": ["Hello!" ] }, } ]}
{"actions": [ {"action_type":"StrikeUser",// or "EndStrikeUser", "RemoveStrikeUser""action_id":"clf10kbhp0012sauvpxlqsb6h",// The Lasso ID of the action"action_created_at":"2023-03-11T15:02:13.178Z",// ISO date of when the action was taken"type":"user", "actor_id": "cldk3z9ze0004saiy542wfbck", // (optional) The Lasso ID of the moderator who triggered this action. Either actor_id or rule_id is set.
"rule_id": "clyhppfjy00574ohv9uigmnia", // (optional) The Lasso ID of the rule which triggered this action. Either actor_id or rule_id is set.
"user": {"id":"cldk3z9ze0004saiy542wfbck",// The product's ID of the user"tags": ["potential-spammer"] // Custom tags attached to the user },// (optional) only on "StrikeUser" or "RemoveStrikeUser""strike": {"count":2,// The total number of strikes the user has// (optional) only on "StrikeUser""duration":3// The number of periods the user will be given a strike, in this case 3 hours// (optional) only on "StrikeUser""period": "hour",// "minute", "hour", "day" or "week"// (optional) only on "StrikeUser""until":"2023-03-11T18:02:13.178Z",// ISO date of when the strike ends } } ]}
Response
The webhook does not expect a certain response, except for a valid 200 status code when action is handled by your product.
When the product does not respond a 200 status after all retries the action(s) will be automatically reverted in Lasso.
Invalid actions
In case there is an invalid action taken (e.g. when an action leads to an error), you can revert the action(s). This will make sure the status of the object remains in sync between the product and Lasso. You can revert actions by returning a list of action ids.
Webhook response
{ revert: ["action_id_1","action_id_2"]}
Retries
In case the webhook fails and receives an error code (3XX, 4XX, 5XX), it will automatically be retried with exponential backoff. Lasso will try to deliver the webhook at most 5 times over the course of a minute.
Security (optional, but recommended)
When Lasso Moderation sends a webhook to your server, it includes a digital signature that you can use to ensure that the payload has not been tampered with.
To verify the signature, you need to compute a hash of the payload using a secret key that you share with Lasso Moderation. The secret key can be found under settings in the Lasso Moderation dashboard. The hash should be computed using the SHA-256 hashing algorithm.
Once you have computed the hash, you can compare it to the signature included in the X-Lasso-Signature header of the webhook request. If the two values match, you can be confident that the payload has not been modified in transit.
Here are some examples of how you might verify the signature of a webhook payload:
import*as crypto from"crypto";functionverifySignature( payload,// body of the request signature,// X-Lasso-Signature from the header):boolean {constsecret=process.env.LASSO_WEBHOOK_SECRET// You need to set this environment variable.consthmac=crypto.createHmac("sha256", secret);consthash=hmac.update(payload).digest("base64");constexpectedSignature=`sha256=${hash}`;return expectedSignature === signature;}
import hmacimport hashlibimport base64defverify_signature(payload:str,signature:str) ->bool:""" Verifies the signature of a webhook request payload. Args: payload (str): The request payload as a string. signature (str): The X-Lasso-Signature header value as a string. Returns: bool: True if the signature is valid, False otherwise. """# You need to set this environment variable. secret = os.environ['LASSO_WEBHOOK_SECRET']# Compute the HMAC digest of the payload using the secret and SHA256 algorithm hmac_digest = hmac.new(secret.encode('utf-8'), payload.encode('utf-8'), hashlib.sha256).digest()# Encode the HMAC digest using base64 expected_signature =f'sha256={base64.b64encode(hmac_digest).decode("utf-8")}'# Compare the computed signature with the signature received in the headerreturn expected_signature == signature
functionverify_signature($payload, $signature) {/** * Verifies the signature of a webhook request payload. * * @paramstring $payload The request payload as a string. * @paramstring $signature The X-Lasso-Signature header value as a string. * * @returnbool True if the signature is valid, False otherwise. */// You need to set this environment variable. $secret =getenv('LASSO_WEBHOOK_SECRET');// Compute the HMAC digest of the payload using the secret and SHA256 algorithm $hmac_digest =hash_hmac('sha256', $payload, $secret,true);// Encode the HMAC digest using base64 $expected_signature ='sha256='.base64_encode($hmac_digest);// Compare the computed signature with the signature received in the headerreturn $expected_signature === $signature;}
importjava.security.InvalidKeyException;importjava.security.NoSuchAlgorithmException;importjavax.crypto.Mac;importjavax.crypto.spec.SecretKeySpec;importjava.util.Base64;publicclassSignatureVerifier { /** * Verifies the signature of a webhook request payload. * * @param payload The request payload as a string. * @param signature The X-Lasso-Signature header value as a string. * @return True if the signature is valid, False otherwise. */publicstaticbooleanverifySignature(String payload,String signature) {// You need to set this environment variable.String secret =System.getenv("LASSO_WEBHOOK_SECRET");try {// Create a new Mac instance specifying the HMAC SHA256 algorithmMac sha256HMAC =Mac.getInstance("HmacSHA256");SecretKeySpec secretKey =newSecretKeySpec(secret.getBytes(),"HmacSHA256");sha256HMAC.init(secretKey);// Compute the HMAC digest of the payload using the secretbyte[] hmacDigest =sha256HMAC.doFinal(payload.getBytes());// Encode the HMAC digest using base64String expectedSignature ="sha256="+Base64.getEncoder().encodeToString(hmacDigest);// Compare the computed signature with the signature received in the headerreturnexpectedSignature.equals(signature); } catch (NoSuchAlgorithmException | InvalidKeyException e) {e.printStackTrace();returnfalse; } }}
Other languages
Please reach out to info@lassomoderation.com in case you would like a code example in a different language.