Webhooks

You can set up FindFace Multi to automatically send notifications about specific events, episodes, and counter screenshots to a given URL. To do so, create and configure a webhook. In this case, when such an event, episode, or a counter screenshot occurs, FindFace Multi will send an HTTP request to the URL configured for the webhook.

You can use webhooks for various purposes, for instance, to notify a user about a specific event, invoke required behavior on a target website, and solve security tasks such as automated access control.

In this section:

Configure Webhook

Important

You need Administrator privileges to create a webhook.

Note

To use the webhooks, make sure that at least one of the following parameters is specified in /opt/findface-multi/configs/findface-multi-legacy/findface-multi-legacy.py: SERVICE_EXTERNAL_ADDRESS or EXTERNAL_ADDRESS.

To create and configure a webhook, do the following:

  1. Navigate to the Settings tab. Click Webhooks.

  2. Click + New webhook.

    create_webhook_en

  3. Specify the webhook header.

    webhook_en

  4. Specify URL to automatically send notifications to.

  5. You can send notifications in batches. Specify the maximum number of notifications in a webhook batch. The actual number may be less.

  6. Specify the maximum number of attempts to send a notification. The interval between attempts increases exponentially with a maximum of 100 seconds.

    Important

    To receive all messages since the connection loss, should it happen, set 0. Set 1 to omit old messages.

  7. FindFace Multi will be automatically sending notifications on events, episodes, and counter screenshots that match given filters. The following filters are available:

    Generic filters for recognition events (face, body, vehicle):

    Sections: face_events, body_events, car_events.

    • "allowed_bs_types": object tracking mode, possible values: overall, realtime.

    • "camera_group_in": camera group id, number.

    • "camera_in": camera id, number.

    • "confidence_gte": minimum confidence, number.

    • "matched_lists_in": watch list id, number.

    • "matched_card_in": matched record id, number.

    • "matched": the matched status. Set true or false if only matched or unmatched events must trigger sending a notification, boolean.

    Filters specific for face recognition events:

    Section: face_events.

    • "liveness_gte": minimum algorithm confidence that a face belongs to a live person, decimal number.

    • "headpose_yaw_angle_gte": minimum head turn, number (degrees). If a head is turned fully to the left, the yaw angle is -180. If it is turned fully to the right, it’s 180. The neutral head position is 0.

    • "headpose_yaw_angle_lte": maximum head turn, number (degrees). If a head is turned fully to the left, the yaw angle is -180. If it is turned fully to the right, it’s 180. The neutral head position is 0.

    • "headpose_pitch_angle_gte": minimum head tilt, number (degrees). If a head is tilted forward with the chin touching the chest, it’s -180. If a head is tilted backward facing the sky, it’s 180. The neutral head position is 0.

    • "headpose_pitch_angle_lte": maximum head tilt, number (degrees). If a head is tilted forward with the chin touching the chest, it’s -180. If a head is tilted backward facing the sky, it’s 180. The neutral head position is 0.

    • "gender_in": gender, dictionary of strings. Possible values: "male", "female".

    • "age_lte": maximum age, number.

    • "age_gte": minimum age, number.

    • "glasses_in": glasses, dictionary of strings. Possible values: "none", "eye" (eyeglasses), "sun" (sunglasses).

    • "emotions_in": emotions, dictionary of strings. Possible values: "any", "angry", "fear", "sad", "neutral", "disgust", "happy", "surprise".

    • "beard_in": beard, dictionary of strings. Possible values: "none", "beard".

    • "medmask_in": face mask, dictionary of strings. Possible values: "correct", "incorrect", "none".

    • "liveness_in": liveness, dictionary of strings. Possible values: "real", "fake".

    Filters specific for body recognition events:

    Section: body_events.

    • "headwear_in": type of headgear (hat/cap, hood/headscarf, none), dictionary of strings. Possible values: "hat", "hood", "none".

    • "upper_clothes_in": generalized category of upper body wear (long sleeves, short sleeves, no sleeve), dictionary of strings. Possible values: "long_sleeves", "short_sleeves", "without_sleeves".

    • "detailed_upper_clothes_in": specific type of upper body wear (jacket, coat, sleeveless vest, sweatshirt, T-shirt, shirt, dress), dictionary of strings. Possible values: "jacket"`, ``"coat", "sleeveless", "sweatshirt", "t-shirt", "shirt", "dress".

    • "top_color_in": top clothing color, dictionary of strings. Possible values: white, "black", "grey", "brown", "red", "orange", "yellow", "green", "lightblue", "blue", "purple", "pink", "beige", "violet".

    • "lower_clothes_in": type of lower body wear (pants, skirt, shorts, nondescript), dictionary of strings. Possible values: "pants", "obscured", "skirt", "shorts".

    • "bottom_color_in": bottom clothing color, dictionary of strings. Possible values: white, "black", "grey", "brown", "red", "orange", "yellow", "green", "lightblue", "blue", "purple", "pink", "beige", "violet".

    • "bag_hand_in": whether a person has a bag in hand(s), dictionary of strings. Possible values: "none", "hand".

    • "bag_back_in": whether a person has a bag on the back, dictionary of strings. Possible values: "none", "back".

    • "vest_type_score_gte": minimum recognition confidence of a vest presence, number.

    • "vest_type_score_lte": maximum recognition confidence of a vest presence, number.

    • "vest_type_in": presence of personal protective equipment in the form of a vest, dictionary of strings. Possible values: "green/yellow", "orange", "not_visible", "none".

    • "helmet_type_score_lte": maximum recognition confidence of a helmet presence, number.

    • "helmet_type_score_gte": minimum recognition confidence of a helmet presence, number.

    • "helmet_type_in": presence of personal protective equipment in the form of a helmet, dictionary of strings. Possible values: "red/orange", "white", "other", "not_visible", "none".

    • "age_group_score_lte": maximum age group recognition confidence, number.

    • "age_group_score_gte": minimum age group recognition confidence, number.

    • "age_group_in": age by group, dictionary of strings. Possible values: "0-16", "17-35", "36-50", "50+".

    • "gender_score_lte": maximum gender recognition confidence, number.

    • "gender_score_gte": minimum gender recognition confidence, number.

    • "gender_in": gender, dictionary of strings. Possible values: "male", "female".

    Filters specific for vehicle recognition events:

    Section: car_events.

    • "category_confidence_gte": minimum recognition confidence of the vehicle category, number.

    • "category_confidence_lte": maximum recognition confidence of the vehicle category, number.

    • "category_type_in": vehicle category, dictionary of strings. Possible values: "unknown", "A", "B", "BE", "C", "CE", "D", "DE", "other".

    • "color_in" vehicle color, dictionary of strings. Possible values: "beige", "black", "blue", "brown", "cyan", "gold", "green", "grey", "orange", "pink", "purple", "red", "violet", "white", "yellow", "coming_soon", "unknown".

    • "body_in": vehicle body style, dictionary of strings. Possible values: "suv", "sedan", "crossover", "convertible", "coupe", "wagon", "cab", "minibus", "minivan", "limousine", "coming_soon", "unknown".

    • "make_in": vehicle make, dictionary of strings. Check out the corresponding event filter to see what makes are supported in the current version.

    • "model_in": vehicle model, dictionary of strings. Check out the corresponding event filter to see what models are supported in the current version.

    • "license_plate_number_in": license plate number, dictionary of strings. Wildcards are not supported.

    • "license_plate_color_in": license plate color, dictionary of strings. Possible values: "unknown", "white", "yellow", "blue", "green", "grey".

    • "license_plate_color_confidence_lte": maximum recognition confidence of the license plate color, number.

    • "license_plate_color_confidence_gte": minimum recognition confidence of the license plate color, number.

    • "license_plate_country_in": license plate country, dictionary of strings. Check out the corresponding event filter to see what countries are supported in the current version.

    • "license_plate_region_in": license plate region, dictionary of strings. Check out the corresponding event filter to see what regions are supported in the current version.

    • "special_vehicle_type_in": special vehicle type, dictionary of strings. Possible values: "taxi", "route_transport", "car_sharing", "ambulance", "police", "rescue_service", "gas_service", "military", "road_service", "other_special", "not_special", "unknown".

    • "weight_type_in": vehicle weight type. Possible values: "B_light", "B_heavy", "C_light", "C_heavy", "D_light", "D_long", "other".

    • "weight_type_confidence_lte": maximum recognition confidence of the vehicle weight type, number.

    • "weight_type_confidence_gte": minimum recognition confidence of the vehicle weight type, number.

    • "orientation_in": vehicle orientation, dictionary of strings. Possible values: "front", "back", "side".

    • "orientation_confidence_lte": maximum recognition confidence of the vehicle orientation, number.

    • "orientation_confidence_gte": minimum recognition confidence of the vehicle orientation, number.

    Episodes with people:

    Section: human_episodes.

    • "allowed_types": episode status, possible values: an episode opening (episode_open), adding a new event into an episode (episode_event), an episode closing (episode_close).

    • "camera_group_in": camera group id, number.

    • "camera_in": camera id, number.

    • "matched_lists_in": watch list id, number.

    • "face_matched": the matched status of face events in an episode, boolean. Set true if only matched faces or false if only unmatched faces must trigger sending a notification.

    • "body_matched": the matched status of body events in an episode, boolean. Set true if only matched bodies or false if only unmatched bodies must trigger sending a notification.

    • "events_count_gte": minimum number of events in an episode, number.

    • "events_count_lte": maximum number of events in an episode, number.

    Episodes with vehicles:

    Section: car_episodes.

    • "allowed_types": episode status, possible values: an episode opening (episode_open), adding a new event into an episode (episode_event), an episode closing (episode_close).

    • "camera_group_in": camera group id, number.

    • "camera_in": camera id, number.

    • "matched_lists_in": watch list id, number.

    • "car_matched": the matched status of car events in an episode, boolean. Set true if only matched cars or false if only unmatched cars must trigger sending a notification.

    • "events_count_gte": minimum number of events in an episode, number.

    • "events_count_lte": maximum number of events in an episode, number.

    Counter screenshots:

    Section: counters.

    • "counter_in": counter id, number.

    • "camera_group_in": camera group id, number.

    • "camera_in": camera id, number.

    • "faces_gte": minimum number of faces in a counter screenshot, number.

    • "faces_lte": maximum number of faces in a counter screenshot, number.

    • "silhouettes_gte": minimum number of bodies in a counter screenshot, number.

    • "silhouettes_lte": maximum number of bodies in a counter screenshot, number.

    • "cars_gte": minimum number of vehicles in a counter screenshot, number.

    • "cars_lte": maximum number of vehicles in a counter screenshot, number.

    • "proximity_min_lte": send a notification if the minimum detected distance in meters is less than this value, number.

    • "proximity_min_gte": send a notification if the minimum detected distance in meters is greater than this value, number.

    • "proximity_avg_lte": send a notification if the average detected distance in meters is less than this value, number.

    • "proximity_avg_gte": send a notification if the average detected distance in meters is greater than this value, number.

    • "proximity_max_lte": send a notification if the maximum detected distance in meters is less than this value, number.

    • "proximity_max_gte": send a notification if the maximum detected distance in meters is greater than this value, number.

    {
    "face_events": {
    "matched_lists_in": [],
    "camera_group_in": [],
    "camera_in": [],
    "matched_card_in": [],
    "matched": true,
    "confidence_gte": 0.75,
    "allowed_bs_types": [
    "overall",
    "realtime"
    ],
    "liveness_gte": 0.65,
    "headpose_yaw_angle_gte": -180,
    "headpose_yaw_angle_lte": 180,
    "headpose_pitch_angle_gte": -180,
    "headpose_pitch_angle_lte": 180,
    "gender_in": [],
    "age_lte": 0,
    "age_gte": 0,
    "glasses_in": [],
    "emotions_in": [],
    "beard_in": [],
    "medmask_in": [],
    "liveness_in": []
    },
    "body_events": {
    "matched_lists_in": [],
    "camera_group_in": [],
    "camera_in": [],
    "matched_card_in": [],
    "matched": true,
    "confidence_gte": 0.75,
    "allowed_bs_types": [
    "overall",
    "realtime"
    ],
    "headwear_in": [],
    "upper_clothes_in": [],
    "detailed_upper_clothes_in": [],
    "top_color_in": [],
    "lower_clothes_in": [],
    "bottom_color_in": [],
    "bag_hand_in": [],
    "bag_back_in": [],
    "vest_type_score_gte": 0,
    "vest_type_score_lte": 0,
    "vest_type_in": [],
    "helmet_type_score_lte": 0,
    "helmet_type_score_gte": 0,
    "helmet_type_in": [],
    "age_group_score_lte": 0,
    "age_group_score_gte": 0,
    "age_group_in": [],
    "gender_score_lte": 0,
    "gender_score_gte": 0,
    "gender_in": []
    },
    "car_events": {
    "matched_lists_in": [],
    "camera_group_in": [],
    "camera_in": [],
    "matched_card_in": [],
    "matched": true,
    "confidence_gte": 0.75,
    "allowed_bs_types": [
    "overall",
    "realtime"
    ],
    "category_confidence_gte": 0,
    "category_confidence_lte": 0,
    "category_type_in": [],
    "color_in": [],
    "body_in": [],
    "make_in": [],
    "model_in": [],
    "license_plate_number_in": [],
    "license_plate_color_in": [],
    "license_plate_color_confidence_lte": 0,
    "license_plate_color_confidence_gte": 0,
    "license_plate_country_in": [],
    "license_plate_region_in": [],
    "special_vehicle_type_in": [],
    "weight_type_in": [],
    "weight_type_confidence_lte": 0,
    "weight_type_confidence_gte": 0,
    "orientation_in": [],
    "orientation_confidence_lte": 0,
    "orientation_confidence_gte": 0
    },
    "human_episodes": {
    "allowed_types": [
    "episode_open",
    "episode_event",
    "episode_close"
    ],
    "matched_lists_in": [],
    "camera_in": [],
    "camera_group_in": [],
    "events_count_gte": 0,
    "events_count_lte": 999,
    "face_matched": true,
    "body_matched": true },
    "car_episodes": {
    "allowed_types": [
    "episode_open",
    "episode_event",
    "episode_close"
    ],
    "matched_lists_in": [],
    "camera_in": [],
    "camera_group_in": [],
    "events_count_gte": 0,
    "events_count_lte": 999,
    "car_matched": true },
    "counters": {
    "counter_in": [],
    "camera_in": [],
    "camera_group_in": [],
    "faces_gte": 1,
    "faces_lte": 100,
    "silhouettes_gte": 1,
    "silhouettes_lte": 100,
    "cars_gte": 1,
    "cars_lte": 100,
    "proximity_min_lte": 100,
    "proximity_min_gte": 0,
    "proximity_avg_lte": 100,
    "proximity_avg_gte": 0,
    "proximity_max_lte": 100,
    "proximity_max_gte": 0
    }
    }
    

    Important

    Use only filters which match your search needs. To turn off a filter, remove it from a webhook. Do not leave a filter empty ([]) as in this case, the result of filtration will be empty as well.

    Note

    To get all notifications, pass only curly braces without any enclosed filters:

    {}
    

    Tip

    Example #1. Get notifications about all vehicle events:

    { "car_events": {} }
    

    Example #2. Get notifications of the opening of matched episodes that feature human faces and bodies:

    { "human_episodes": { "allowed_types": ["episode_open"], "face_matched": true, "body_matched": true }}
    

    Note

    You can specify several values for filters with square braces. In this case, the webhook will be triggered once one of the values from this filter has been matched. In the example below, you will get a body event from the camera group 1 or 3 if a matched record is 12 or 25.

    {
            "body_events": {
                    "camera_group_in": [1, 3],
                    "matched_card_in": [12, 25],
            },
    }
    
  8. Check Active.

  9. Click Save.

Webhook in Action

Try out a webhook by capturing event notifications with a simple web server in Python:

from pprint import pprint
from aiohttp import web


async def handle(request):
    pprint(await request.json())
    return web.Response(status=200)


app = web.Application()
# for aiohttp v 3.x
# app.add_routes([web.post('/', handle)])

# for aiohttp v 2.x
app.router.add_post('/', handle)

web.run_app(app, port=8888)

Important

A webhook catcher that you use must return an HTTP 200 response after receiving the webhook request from FindFace Multi, like in the example above.

If no filters are configured for a webhook, this web server will be getting notifications about all events, episodes, and counter screenshots.

To view a webhook pulling status, execute:

sudo journalctl CONTAINER_NAME=findface-multi-findface-multi-legacy-1 -f | grep 'Webhook'

Verbose Webhooks

By default, webhook notifications contain only ids of such entities as cards, watch lists, cameras, and camera groups. It is possible to get the full content of the mentioned entities by switching webhooks to the verbose mode.

To do so, open the /opt/findface-multi/configs/findface-multi-legacy/findface-multi-legacy.py configuration file and set 'VERBOSE_WEBHOOKS': True:

sudo vi /opt/findface-multi/configs/findface-multi-legacy/findface-multi-legacy.py

...
FFSECURITY = {
    ...
    # send serialized cards, card-lists, camera and camera groups in webhooks
    'VERBOSE_WEBHOOKS': True,
    ...
}
...

Be sure to restart the findface-multi-findface-multi-legacy-1 container after making changes.

sudo docker container restart findface-multi-findface-multi-legacy-1