Webhooks
You can set up FindFace Multi to automatically send notifications about specific events, episodes, counter records, and area activations to a given URL. To do so, create and configure a webhook. In this case, when such an event, episode, counter record, or an area activation 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 /etc/findface-security/config.py
: SERVICE_EXTERNAL_ADDRESS
or EXTERNAL_ADDRESS
.
To create and configure a webhook, do the following:
Navigate to the Preferences tab. Click Webhooks.
Click +.
Specify the webhook title.
Specify URL to automatically send notifications to.
You can send notifications in batches. Specify the maximum number of notifications in a webhook batch. The actual number may be less.
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
. Set1
to omit old messages.FindFace Multi will be automatically sending notifications on events, episodes, counter records, and area activations that match given filters. The following filters are available:
Recognition events (face, body, car):
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.matched_lists_in
: watch list id, number.matched_card_in
: matched card id, number.matched
: thematched
status. Settrue
orfalse
if only matched or unmatched events must trigger sending a notification, boolean.confidence_gte
: minimum confidence, 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
: thematched
status of face events in an episode, boolean. Settrue
if only matched faces orfalse
if only unmatched faces must trigger sending a notification.body_matched
: thematched
status of body events in an episode, boolean. Settrue
if only matched bodies orfalse
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 cars:
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
: thematched
status of car events in an episode, boolean. Settrue
if only matched cars orfalse
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 records:
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 record, number.faces_lte
: maximum number of faces in a counter record, number.silhouettes_gte
: minimum number of bodies in a counter record, number.silhouettes_lte
: maximum number of bodies in a counter record, number.cars_gte
: minimum number of cars in a counter record, number.cars_lte
: maximum number of cars in a counter record, 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.
Area activations:
Section:
areas
.area_in
: area id, number.camera_group_in
: camera group id, number.camera_in
: camera id, number.
{ "face_events": { "allowed_bs_types": [ "overall", "realtime" ], "camera_group_in": [], "camera_in": [], "matched_lists_in": [], "matched_card_in": [], "matched": true, "confidence_gte": 0.75 }, "body_events": { "allowed_bs_types": [ "overall", "realtime" ], "camera_group_in": [], "camera_in": [], "matched_lists_in": [], "matched_card_in": [], "matched": true, "confidence_gte": 0.75 }, "car_events": { "allowed_bs_types": [ "overall", "realtime" ], "camera_group_in": [], "camera_in": [], "matched_lists_in": [], "matched_card_in": [], "matched": true, "confidence_gte": 0.75 }, "human_episodes": { "allowed_types": [ "episode_open", "episode_event", "episode_close" ], "camera_group_in": [], "camera_in": [], "matched_lists_in": [], "face_matched": true, "body_matched": true, "events_count_gte": 0, "events_count_lte": 999 }, "car_episodes": { "allowed_types": [ "episode_open", "episode_event", "episode_close" ], "camera_group_in": [], "camera_in": [], "matched_lists_in": [], "car_matched": true, "events_count_gte": 0, "events_count_lte": 999 }, "counters": { "counter_in": [], "camera_group_in": [], "camera_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 }, "areas": { "area_in": [], "camera_group_in": [], "camera_in": [] } }
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 car 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
or3
if a matched card is12
or25
.{ "body_events": { "camera_group_in": [1, 3], "matched_card_in": [12, 25] } }
Check Active.
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, counter records, and area notifications that occur in the system. The notifications have the following formats:
To view a webhook pulling status, execute:
sudo journalctl -u findface-security.service | grep 'Webhook'
Success:
May 30 14:13:43 ffsecurity[12441]: INFO [Webhook(id=6) worker(type=face_events] <queue: 0> Sent batch(len-1, type-"face_events"): ['4355024961160384430']
May 30 14:13:43 ffsecurity[12441]: INFO [SC:OQSrsPV9] [Webhooks manager-38bc5] Processing message(type="face_events:event_created"). Consumer reception delta: 0.003450
May 30 14:13:43 ffsecurity[12441]: INFO [Webhook(id=6) worker(type=face_events] <queue: 0> Sent batch(len-1, type-"face_events"): ['4355024961658847580']
May 30 14:13:44 ffsecurity[12441]: INFO [SC:JtRz2Vuo] [Webhooks manager-38bc5] Processing message(type="face_events:event_created"). Consumer reception delta: 0.001263
May 30 14:13:44 ffsecurity[12441]: INFO [Webhook(id=6) worker(type=face_events] <queue: 0> Sent batch(len-1, type-"face_events"): ['4355024962087522421']
May 30 14:13:44 ffsecurity[12441]: INFO [SC:9AnzRJwU] [Webhooks manager-38bc5] Processing message(type="face_events:event_created"). Consumer reception delta: 0.001691
May 30 14:13:44 ffsecurity[12441]: INFO [Webhook(id=6) worker(type=face_events] <queue: 0> Sent batch(len-1, type-"face_events"): ['4355024962355957878']
Failure:
May 30 14:18:49 ffsecurity[12441]: INFO [SC:sp34rVQR] [Webhooks manager-38bc5] Processing message(type="face_events:event_created"). Consumer reception delta: 0.001376
May 30 14:18:49 ffsecurity[12441]: WARNING [Webhook(id=6) worker(type=face_events] <queue: 0> Error sending webhook: Cannot connect to host 127.0.0.1:8888 ssl:None [Connection refused]. Attempt 1 out of 10. Next attempt in 0.270 seconds.
May 30 14:18:50 ffsecurity[12441]: WARNING [Webhook(id=6) worker(type=face_events] <queue: 0> Error sending webhook: Cannot connect to host 127.0.0.1:8888 ssl:None [Connection refused]. Attempt 2 out of 10. Next attempt in 0.729 seconds.
May 30 14:18:50 ffsecurity[12441]: INFO [SC:zUhLHNxN] [Webhooks manager-38bc5] Processing message(type="face_events:event_created"). Consumer reception delta: 0.001368
May 30 14:18:50 ffsecurity[12441]: INFO [SC:1Q66tcUS] [Webhooks manager-38bc5] Processing message(type="face_events:event_created"). Consumer reception delta: 0.001386
May 30 14:18:50 ffsecurity[12441]: WARNING [Webhook(id=6) worker(type=face_events] <queue: 2> Error sending webhook: Cannot connect to host 127.0.0.1:8888 ssl:None [Connection refused]. Attempt 3 out of 10. Next attempt in 1.968 seconds.
May 30 14:18:52 ffsecurity[12441]: WARNING [Webhook(id=6) worker(type=face_events] <queue: 2> Error sending webhook: Cannot connect to host 127.0.0.1:8888 ssl:None [Connection refused]. Attempt 4 out of 10. Next attempt in 5.314 seconds.
May 30 14:18:55 ffsecurity[12441]: INFO [SC:5kl6zGrF] [Webhooks manager-38bc5] Processing message(type="face_events:event_created"). Consumer reception delta: 0.001542
May 30 14:18:58 ffsecurity[12441]: WARNING [Webhook(id=6) worker(type=face_events] <queue: 3> Error sending webhook: Cannot connect to host 127.0.0.1:8888 ssl:None [Connection refused]. Attempt 5 out of 10. Next attempt in 14.349 seconds.
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 /etc/findface-security/config.py
configuration file and set 'VERBOSE_WEBHOOKS': True
:
sudo vi /etc/findface-security/config.py
...
FFSECURITY = {
...
# send serialized cards, card-lists, camera and camera groups in webhooks
'VERBOSE_WEBHOOKS': True,
...
}
...
Be sure to restart the findface-security
service after making changes.
sudo systemctl restart findface-security.service
In the verbose mode, webhook notifications have the following formats: