Skip to main content

Webhooks (v1.4)

Welcome to the Entrupy API!

Overview

This page contains documentation for how to create and modify webhooks. Webhooks allow you to receive push events whenever an authentication is created or changed. Webhooks can be created for all authentications created by your organization, or for a specific user.

For complete documentation of the rest of the Entrupy API including how to search item history, see the Entrupy API v1.4 docs.

Information on the format of webhook events can be found in the v1.4 data model, and information on other API routes can be found in the v1.4 API Reference.

Getting Started with Webhooks

The Entrupy API serves all requests over HTTPS from api.entrupy.com.

An authorization token is required. Authorize your requests with the header Authorization: Token 01234567, replacing 01234567 with your token.

When making your request to create a webhook, specify the desired API Version with the required header Api-Version: 1.4. Currently, the API only supports version 1.3 and above when creating webhooks.

You can test your credentials and check service health by making a request to the following path:

GET /v1/health

Basic check to test credentials and view server status.

Example request:

GET /v1/health HTTP/1.1
Host: api.entrupy.com
Api-Version: 1.4
Authorization: Token [valid_token]

Example response:

HTTP/1.1 200 OK
Content-Type: application/json

{
"status": "ok"
}
  • Request Headers:
    • Api-Version: 1.4
    • Authorization: required token
  • Status Codes:
    • 200 OK: Authorized, service ok
    • 400 Bad Request: Incorrect headers
    • 401 Unauthorized: Route is unauthorized (token was invalid or not provided)
    • 429 Too Many Requests: Rate limit has been exceeded. The Retry-After header will contain the number of seconds to wait before retry.

Webhook API Routes

Info

This section of the document describes what routes are available for creating and managing webhooks. For more details about how you should setup your backend server to process webhook events, see the Webhook Endpoint Setup section at the end of this documentation.

Create a New Webhook

Corresponds to edit_webhooks.route__v1__webhook__create.

POST /v1/webhooks

Create a new webhook.

The following arguments are required: The HTTPS endpoint URL where events should be sent, a secret key, and a list of filters. You can also set an optional channel name.

The secret key that you send will be used to sign all webhook payloads for this webhook that get sent to your endpoint, so that you can verify that they are legitimate webhook payloads from Entrupy. It must be at least 20 random characters to prevent spoofing.

URLs with domains not registered to your organization will be rejected.

For more information on how register a domain, generating secret keys, and message validation see Webhook Endpoint Setup.

You cannot have more than 10 active webhooks at a time. If you reach this limit a 409 error will be returned and you should deactivate any webhooks which are not currently in use before creating a new one.

Example Request A:

This creates a webhook that will send events for any authentication owned by your organization. Events will be sent to the URL https://sample-endpoint.entrupy.com/notifications.

POST /v1/webhooks HTTP/1.1
Host: api.entrupy.com
Api-Version: 1.4
Authorization: Token [valid_token]
Content-Type: application/json

{
"endpoint": {
"type": "url",
"value": "https://sample-endpoint.entrupy.com/notifications"
},
"secret_key": "Example-Secret-Key-McEUC7uV8fUzkvtV",
"filters": [
{"key": "activity.name", "value": "authentication"}
]
}

Example response A:

HTTP/1.1 200 OK
Content-Type: application/json

{
"channel": "default",
"create_timestamp": {
"display": "2024-05-15T14:06:58.136977Z",
"epoch": 1715782018.136977
},
"endpoint": {
"type": "url",
"value": "https://sample-endpoint.entrupy.com/notifications"
},
"filters": [
{"key": "activity.name", "value": "authentication"}
],
"webhook_uuid": "cff3fe0e-122a-4cb6-8b73-465c529d8b02",
"webhook_owner": {
"organization": {"name": "EntrupyOrganization"},
"user": {"username": "EntrupyUser"}
}
}

Example Request B:

This creates a webhook that will send events for all authentications created by the user EntrupyUser. This username must exist in your organization or an error will be returned when you try to create the webhook. Events will be sent to the URL https://sample-endpoint.entrupy.com/notifications. The provided channel name "example_channel" will be included with all events generated by this webhook.

POST /v1/webhooks HTTP/1.1
Host: api.entrupy.com
Api-Version: 1.4
Authorization: Token [valid_token]
Content-Type: application/json

{
"channel": "example_channel",
"endpoint": {
"type": "url",
"value": "https://sample-endpoint.entrupy.com/notifications"
},
"filters": [
{"key": "activity.name", "value": "authentication"},
{"key": "owner.user.username", "value": "EntrupyUser"}
],
"secret_key": "Example-Secret-Key-McEUC7uV8fUzkvtV"
}

Example response B:

HTTP/1.1 200 OK
Content-Type: application/json

{
"channel": "example_channel",
"create_timestamp": {
"display": "2024-05-15T14:06:58.136977Z",
"epoch": 1715782018.136977
},
"endpoint": {
"type": "url",
"value": "https://sample-endpoint.entrupy.com/notifications"
},
"filters": [
{"key": "activity.name", "value": "authentication"},
{"key": "owner.user.username", "value": "EntrupyUser"}
],
"webhook_owner": {
"organization": {"name": "EntrupyOrganization"},
"user": {"username": "EntrupyUser"}
},
"webhook_uuid": "f3131c4a-7f89-4ac0-b80c-795070dcce14"
}
  • Parameters:
    • endpoint: (Required) The endpoint to which data will be sent.
      • type: This must be the string "url"
      • value: The URL to send notifications to. This must be a HTTPS URL on a domain that you have registered for webhooks.
    • secret_key: (Required) The secret key used to create the message hash for events. Must be at least 20 random characters to prevent spoofing.
    • channel: An arbitrary string which will be returned with all messages for this webhook. Channels can be used to disambiguate messages from different webhooks that are sent to the same URL. Default value "default".
    • filters: A list of filter dicts used to narrow down which authentication / fingerprint push notifications will be sent to this endpoint. You must include a filter for "activity.name". Default: []. Each dict in the filters list must have the following keys:
      • key: One of the following:
        • activity.name (Valid values: "authentication" or "fingerprint")
        • activity.form_factor (Valid values: "lightbox", "microscopic", or "free_camera" )
        • owner.user.username (Valid values: Any username in your organization)
      • value: A string that will be matched. Exactly one of value or values is required.
      • values: A list of strings that will be matched. Exactly one of value or values is required.
  • Request Headers:
    • Api-Version: 1.4
    • Authorization: required token
    • Accept-Language: "en" for English or "ja" for Japanese. No other languages are supported currently. Default "en".
  • Status Codes:
    • 200 OK: Webhook successfully created, no error
    • 400 Bad Request: Incorrect headers or invalid JSON
    • 401 Unauthorized: Route is unauthorized (token was invalid or not provided)
    • 403 Forbidden: Some parameters were invalid for your token. For example, the username you are trying to filter by is not part of your organization, or the domain name of endpoint is not registered with your organization.
    • 409 Conflict: Returned if you are trying to create more than 10 webhooks.

View a Webhook

Corresponds to edit_webhooks.route__v1__webhooks__webhook_uuid.

GET /v1/webhooks/<webhook_uuid>

Returns configuration and status for the webhook webhook_uuid.

This route only returns information for webhooks owned by the requester. Invalid Webhook UUIDs or UUIDs owned by someone else will return a 403.

Example Request:

GET /v1/webhooks/cff3fe0e-122a-4cb6-8b73-465c529d8b02 HTTP/1.1
Host: api.entrupy.com
Api-Version: 1.4
Authorization: Token [valid_token]

Example response:

HTTP/1.1 200 OK
Content-Type: application/json

{
"channel": "default",
"create_timestamp": {
"display": "2024-05-15T14:06:58.136977Z",
"epoch": 1715782018.136977
},
"endpoint": {
"type": "url",
"value": "https://sample-endpoint.entrupy.com/notifications"
},
"filters": [
{"key": "activity.name", "value": "authentication"}
],
"webhook_owner": {
"organization": {"name": "EntrupyOrganization"},
"user": {"username": "EntrupyUser"}
},
"webhook_uuid": "cff3fe0e-122a-4cb6-8b73-465c529d8b02"
}
  • Path Parameters:
    • webhook_uuid: The UUID of the webhook to retrieve.
  • Request Headers:
    • Api-Version: 1.4
    • Authorization: required token
  • Status Codes:
    • 200 OK: Webhook retrieved successfully
    • 400 Bad Request: Incorrect headers
    • 401 Unauthorized: Route is unauthorized (token was invalid or not provided)
    • 403 Forbidden: Either the webhook with the provided ID does not exist, or you do not have access to it.

Deactivate a Webhook

Corresponds to edit_webhooks.route__v1__webhooks__webhook_uuid__deactivate.

POST /v1/webhooks/<webhook_uuid>/deactivate

Deactivate the webhook webhook_uuid.

The webhook must be an active webhook owned by the requestor.

Example Request:

POST /v1/webhooks/a58e4165-6217-47f2-beb4-ab74d8f1da55/deactivate HTTP/1.1
Host: api.entrupy.com
Api-Version: 1.4
Authorization: Token [valid_token]

Example response:

HTTP/1.1 200 OK
Content-Type: application/json

{
"status": "ok"
}
  • Path Parameters:
    • webhook_uuid: The UUID of the webhook to deactivate.
  • Request Headers:
    • Api-Version: 1.4
    • Authorization: required token
  • Status Codes:
    • 200 OK: Webhook was owned by the requestor and was successfully deactivated.
    • 400 Bad Request: Incorrect headers or invalid parameters
    • 401 Unauthorized: Route is unauthorized (token was invalid or not provided)
    • 403 Forbidden: Either the webhook with the provided ID does not exist, or you do not have access to it.

Search Webhooks

Corresponds to edit_webhooks.route__v1__search__webhooks.

POST /v1/search/webhooks

Search for active webhooks created by the requesting organization.

Example Request:

POST /v1/search/webhooks HTTP/1.1
Host: api.entrupy.com
Api-Version: 1.4
Authorization: Token [valid_token]
Content-Type: application/json

{
"limit": 10
}

Example response:

HTTP/1.1 200 OK
Content-Type: application/json

{
"item_count": 2,
"items": [
{
"channel": "authentication-channel",
"create_timestamp": {
"display": "2024-05-15T14:06:58.136977Z",
"epoch": 1715782018.136977
},
"endpoint": {
"type": "url",
"value": "https://sample-endpoint.entrupy.com/notifications"
},
"filters": [
{"key": "activity.name", "value": "authentication"}
],
"webhook_owner": {
"organization": {"name": "EntrupyOrganization"},
"user": {"username": "EntrupyUser"}
},
"webhook_uuid": "cff3fe0e-122a-4cb6-8b73-465c529d8b02"
},
{
"channel": "fingerprint-channel",
"create_timestamp": {
"display": "2024-05-15T14:06:58.136977Z",
"epoch": 1715782018.136977
},
"endpoint": {
"type": "url",
"value": "https://sample-endpoint.entrupy.com/notifications"
},
"filters": [
{"key": "activity.name", "value": "fingerprint"}
],
"webhook_owner": {
"organization": {"name": "EntrupyOrganization"},
"user": {"username": "EntrupyUser"}
},
"webhook_uuid": "f3131c4a-7f89-4ac0-b80c-795070dcce14"
}
],
"next_cursor": null
}
  • Parameters:
    • limit: The maximum number of items to be returned. Default: 25. Maximum: 25.
    • cursor: Used for pagination. Set this value to the previous request's next_cursor field. Default: null.
  • Request Headers:
    • Api-Version: 1.4
    • Authorization: required token
  • Status Codes:
    • 200 OK: Webhooks listed successfully
    • 400 Bad Request: Incorrect headers
    • 401 Unauthorized: Route is unauthorized (token was invalid or not provided)

Search Registered Domains

Corresponds to domain_registration.v1__search__domains.

POST /v1/search/domains

List all domain names registered as belonging to your organization.

Example request:

POST /v1/search/domains HTTP/1.1
Host: api.entrupy.com
Api-Version: 1.4
Authorization: Token [valid_token]
Content-Type: application/json

{
"limit": 10
}

Example response:

HTTP/1.1 200 OK
Content-Type: application/json

{
"item_count": 2,
"items": [
{
"create_timestamp": {
"display": "2017-05-15T14:06:58.136977Z",
"epoch": 1494871618.136977
},
"domain_name": "sample-endpoint.entrupy.com",
"domain_owner": {
"organization": {"name": "Sample Organization"},
"user": {"username": "Sample User"}
},
"domain_uuid": "b9b39947-8a16-483d-a7eb-2b961c41b2f7"
},
{
"create_timestamp": {
"display": "2017-05-15T14:06:58.136977Z",
"epoch": 1494871618.136977
},
"domain_name": "sample-endpoint-2.entrupy.com",
"domain_owner": {
"organization": {"name": "Sample Organization"},
"user": {"username": "Sample User"}
},
"domain_uuid": "7b0699d7-0c00-41e7-bc51-259e50537665"
}
],
"next_cursor": null
}
  • Parameters:
    • limit: The maximum number of items to be returned. Default: 25. Maximum: 25.
    • cursor: Used for pagination. Set this value to the previous request's next_cursor field. Default: null.
  • Request Headers:
    • Api-Version: 1.4
    • Authorization: required token
  • Status Codes:
    • 200 OK: Domains listed successfully
    • 400 Bad Request: Incorrect headers
    • 401 Unauthorized: Route is unauthorized (token was invalid or not provided)

Webhook Endpoint Setup

Instead of polling our API to check the current status of an authentication, you can create a webhook and setup a webhook endpoint server that receives push notifications. Whenever a relevant authentication is created or updated, a POST request is sent to the configured endpoint URL. If the endpoint can't be reached, the system will retry at increasing intervals until the message is successfully sent or the maximum delivery time has elapsed.

Using webhooks requires registration with Entrupy by contacting us at developer@entrupy.com, as well as configuration on your server. Your server must also have a publicly accessible HTTPS URL.

Steps for Creating a Webhook Endpoint Server

Request a Registered Domain Name

Webhooks can only be configured to send events to domain names that you control.

Currently, there is no automated system to register domains. Contact Entrupy's engineering team at developer@entrupy.com to register a domain name for your organization. We will ask you to validate your ownership of the domain by giving you a long random string and a specific page on that domain (Such as https://<YOUR_DOMAIN>/.well-known/entrupy-domain-verification/<TOKEN>) which must return the string we gave you in response to a GET request. Once we have verified that this page has been setup correctly, we will update our database so you can start creating webhooks with this domain.

You can view which domains are currently registered for your organization with the Search Registered Domains route.

Verifying Incoming Events

All webhook events are signed with a cryptographic hash (HMAC) using the secret key that you provide when you first create the webhook. This hash allows your endpoint to verify that any events received at your endpoint were sent by Entrupy and not modified in transit. This secret key needs to be submitted to us when creating a webhook, and also saved on the server that will be receiving webhook events. We recommend you pick a secret key of at least 20 random characters. Your webhook endpoint must also have an HTTPS url so that data we send to your server is secure.

We will always set a Entrupy-Signature header which specifies a hash function (this is currently always SHA256), and a base64 encoded signature of an event. Example:

Entrupy-Signature: SHA256:baE+ukffjr0To19SYmqGeHFilr61oKaiC1nF4Ubjbww=

You should independently recalculate this HMAC using your secret key and verify that it matches. Here is an example of one way to do this in python, using snippets from the python server example:

  • Get the string data from a POST request:
string_data = flask.request.data.decode('utf-8')
  • Calculate an HMAC using your secret key and the SHA256 hash:
import hmac
import hashlib

binary_signature = hmac.new(
key=bytearray(YOUR_SECRET_KEY, 'utf-8'),
msg=bytearray(string_data, 'utf-8'),
digestmod=hashlib.sha256
).digest()
  • Encode the result as a base64 string to match the header we sent:
import base64
correct_hash = base64.b64encode(binary_signature).decode('utf-8')
  • Check what signature was sent by the client:
signature = flask.request.headers['Entrupy-Signature']
signature_tokens = signature.split(':')
assert len(signature_tokens) == 2
hash_function, hash_from_header = signature_tokens
assert hash_function == 'SHA256'
  • Verify that the signature sent to your server matches the signature you calculated:
assert hmac.compare_digest(correct_hash, hash_from_header), 'Hash Mismatch!'

Processing Webhook Events

Webhook Endpoint Server Configuration

The server can listen at any URL you want, as long as the domain name has been registered and it is serving over HTTPS. The route receiving events must accept the POST method.

Make sure that you return HTTP status 200 if the webhook was successfully processed.

No response, a timeout, and any 400 or 500 series status from your server is considered a temporary failure and another update attempt will be made later.

Here is a barebones example of what your webhook endpoint might look like in python, using Flask:

Python Server Example
import os
import sys
import hmac
import base64
import hashlib
import flask

"""
This code has been tested to work with python3.11
To run this, you need to first install the following dependencies with pip:

pip install blinker==1.7.0
pip install click==8.1.7
pip install Flask==3.0.0
pip install itsdangerous==2.1.2
pip install Jinja2==3.1.2
pip install MarkupSafe==2.1.3
pip install Werkzeug==3.0.1

Also export the secret key you selected for signing messages with
`export WEBHOOK_ENDPOINT_SECRET_KEY="yourkeyhere"`
"""

try:
WEBHOOK_ENDPOINT_SECRET_KEY = os.environ['WEBHOOK_ENDPOINT_SECRET_KEY']
except KeyError:
print("export the variable WEBHOOK_ENDPOINT_SECRET_KEY first")
sys.exit(1)

application = flask.Flask(__name__)
application.config['JSONIFY_PRETTYPRINT_REGULAR'] = False


def sign_payload(string_data, secret_key):
encoded_secret_key = bytearray(secret_key, 'utf-8')
encoded_string_data = bytearray(string_data, 'utf-8')
binary_signature = hmac.new(
key=encoded_secret_key,
msg=encoded_string_data,
digestmod=hashlib.sha256
).digest()
return base64.b64encode(binary_signature).decode('utf-8')


def validate_data(string_data, signature):
signature_tokens = signature.split(':')
assert len(signature_tokens) == 2
hash_function, hash_from_header = signature_tokens
# We will always specify the hash function used to sign data. For now, it will always be SHA256
assert hash_function == 'SHA256'

correct_hash = sign_payload(string_data, WEBHOOK_ENDPOINT_SECRET_KEY)
# If the HMAC does not match our signature, this was not a valid message.
assert hmac.compare_digest(correct_hash, hash_from_header), 'Hash Mismatch! This message might not have come from Entrupy, please verify that you are using the correct secret key.'


@application.route('/health')
def route__health():
return flask.jsonify(status='ok')


@application.route('/notify', methods=['POST'])
def route__notify():
string_data = flask.request.data.decode('utf-8')
try:
sender_signature = flask.request.headers['Entrupy-Signature']
validate_data(string_data, sender_signature)
except (AssertionError, KeyError):
print("Unauthorized")
return flask.jsonify(error='unauthorized'), 401
print("Got data: {}".format(string_data))
# TODO: Add code here to process this webhook event, save it to a database, etc.
return flask.jsonify(status='ok'), 200


def main():
if len(sys.argv) != 3:
print(f"Usage: python {sys.argv[0]} <ADDRESS [example: localhost]> <PORT [example: 8080]>")
else:
print("Listening on http://{}:{}/notify".format(sys.argv[1], int(sys.argv[2])))
application.run(host=sys.argv[1], port=int(sys.argv[2]))


if __name__ == "__main__":
main()

Message Data Format

The data sent to your webhook endpoint will have a similar format to that returned from normal searches. See the Entrupy API v1.4 search route documentation for more information about the data format.

These are the only differences:

  • There is an additional field, channel, which you can use to recognize which webhook the message came from. By default channel will be "default". If you set channel to a string when creating a webhook, then the same channel will be returned with the event. This allows you to have multiple webhooks delivering payloads to the same URL if required.
  • There is a field webhook_uuid which is also returned when you search webhooks using our API. You can use this to see which webhook an event came from even if the channel is not set.
  • There are no next or previous cursors.
  • Fingerprint items will not include the fingerprint_parent key.
  • Every item in the items list we send will have a monotonically increasing update count field, update_counter. Since we cannot guarantee that events will reach your servers in order, you should store the update_counter value in your systems and discard any events received with a lower update_counter than your current record.

Example Webhook Payload:

{
"channel": "sample_channel",
"item_count": 1,
"webhook_uuid": "da2b715c-1850-43c8-bb0c-96f5ed90a363",
"items": [
{
"entrupy_id": "M96JT72",
"owner": {
"organization": {
"name": "EntrupyOrganization"
},
"user": {
"username": "EntrupyUser"
}
}
// ... other item fields ...
}
]
}

Webhook Event Types

To lookup items by the customer_item_id provided when the item was submitted, see the documentation on the lookup route.