Open In Colab
GitHub
Example configurations to receive requests
from flask import Flask, request
import json
import hmac
import hashlib
import threading
from werkzeug.serving import run_simple
# This can be any secret that matches your webhook config (we will set later)
secret = b"<replace with secret>"
# Example for server-side code to receive webhook events
app = Flask(__name__)
@app.route("/webhook-endpoint", methods=["POST"])
def print_webhook_info():
payload = request.data
computed_signature = hmac.new(
secret, msg=payload, digestmod=hashlib.sha1
).hexdigest()
if request.headers["X-Hub-Signature"] != "sha1=" + computed_signature:
print(
"Error: computed_signature does not match signature provided in the headers"
)
return "Error", 500
print("=========== New Webhook Delivery ============")
print("Delivery ID: %s" % request.headers["X-Labelbox-Id"])
print("Event: %s" % request.headers["X-Labelbox-Event"])
print(
"Payload: %s"
% json.dumps(json.loads(payload.decode("utf8")), indent=4)
)
return "Success"
thread = threading.Thread(
target=lambda: run_simple("0.0.0.0", 3001, app)
)
thread.start()
import hmac
import hashlib
import re
secret = "<replace with secret>"
# Example of a AWS Lambda to receive webhook events
def lambda_handler(event, context):
webhook = re.sub(
r"^sha1=",
"",
event["headers"]["x-hub-signature"],
)
digest = hmac.new(
key=bytes(secret, "utf-8"),
msg=event["body"].encode("utf-8"),
digestmod=hashlib.sha1,
)
if webhook != digest.hexdigest():
return {
"statusCode": 400,
"body": "Error: computed_signature does not match",
}
print("=========== New Webhook Delivery ============")
print("Delivery ID: %s" % event["headers"]["x-labelbox-id"])
print("Event: %s" % event["headers"]["x-labelbox-event"])
print("=========== Payload =============")
print(event["body"])
return "Success"
import functions_framework
import json
import hmac
import hashlib
secret = b"<replace with secret>"
# Example of a Google Cloud Function to receive webhook events
@functions_framework.http
def hello_http(request):
payload = request.data
computed_signature = hmac.new(
secret, msg=payload, digestmod=hashlib.sha1
).hexdigest()
if request.headers["X-Hub-Signature"] != "sha1=" + computed_signature:
print(
"Error: computed_signature does not match signature provided in the headers"
)
return "Error", 500
print("=========== New Webhook Delivery ============")
print("Delivery ID: %s" % request.headers["X-Labelbox-Id"])
print("Event: %s" % request.headers["X-Labelbox-Event"])
print(
"Payload: %s"
% json.dumps(json.loads(payload.decode("utf8")), indent=4)
)
return "Success"
import azure.functions as func
import json
import hmac
import logging
import hashlib
secret = b"<replace with secret>"
# Example of an Azure Function to receive webhook events
app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)
@app.route(route="http_trigger")
def http_trigger(req: func.HttpRequest) -> func.HttpResponse:
payload = req.get_body()
computed_signature = hmac.new(
secret, msg=payload, digestmod=hashlib.sha1
).hexdigest()
if req.headers["X-Hub-Signature"] != "sha1=" + computed_signature:
logging.warning(
"Error: computed_signature does not match signature provided in the headers"
)
return func.HttpResponse("Error", status_code=500)
logging.info("=========== New Webhook Delivery ============")
logging.info("Delivery ID: %s" % req.headers["X-Labelbox-Id"])
logging.info("Event: %s" % req.headers["X-Labelbox-Event"])
logging.info(
"Payload: %s"
% json.dumps(json.loads(payload.decode("utf8")), indent=4)
)
return func.HttpResponse("Success", status_code=200)
Create webhook
Webhook v2 setup
You can create a v2 webhook through the Labelbox UI. The v2 webhook mimics the export payload.Webhook v1 setup
As shown below, you can create a v1 webhook with the Python SDK or the Labelbox UI, which is similar to v2 webhooks.from labelbox import Client, Webhook
public_url = "https://example.com/webhook-endpoint" # Your server's public url - where the messages will be sent to
secret = b"CHANGE-ME" # Use to verify Labelbox is sending the message
client = Client(api_key="<YOUR_API_KEY>")
project = client.get_project("<project_id>")
print([topic.value for topic in Webhook.Topic]) # See all available topics
secret = b"example_secret" # This can be any secret that matches your webhook config (we will set later)
webhook = Webhook.create(client,
topics=["LABEL_CREATED"],
url="public_url",
secret=secret.decode(),
project=project)
Get webhooks
# Fetch all webhooks
org = client.get_organization()
webhooks = org.webhooks() #paginated
# Fetch project webhooks
project = client.get_project("<project_id>")
webhooks = project.webhooks() #paginated
webhook = next(webhooks)
status = webhook.status
server_url = webhook.url
topics = webhook.topics
project = webhook.project()
Update webhook
# url, topics, and status can all be updated
updated_url = f"{public_url}/webhook-endpoint"
webhook.update(url=updated_url, topics=[Topic.LABEL_DELETED], status=Webhook.Status.INACTIVE.value)
Delete webhook
webhook.delete()
Sample response
v2
{
"data_row": {
"id": "clvmpaetz76ct0706730rfcq9",
"external_id": null,
"global_key": "TEST-ID-157128604429964085398241585654637789196d",
"row_data": "https://storage.googleapis.com/labelbox-datasets/People_Clothing_Segmentation/jpeg_images/IMAGES/img_0001.jpeg",
"metadata_fields": [],
"details": {
"dataset_id": "clvmpa9et010j0750rlgs8qdi",
"created_at": "2024-04-30T18:07:10.82Z",
"updated_at": "2024-04-30T18:07:14.199Z",
"created_by": null
}
},
"projects": {
"clvo7ajpc014907zxagiw9hry": {
"project_name": "test_lambda",
"labels": [
{
"label_kind": "Default",
"id": "clvo7b6h501ic07fdbg62c32w",
"label_details": {
"created_at": "2024-05-06T17:18:48Z",
"updated_at": "2024-05-06T17:18:48Z",
"created_by": "[email protected]"
},
"performance_details": {
"seconds_to_create": 5759.536,
"seconds_to_review": 0,
"skipped": false,
"benchmark_reference_label": null,
"benchmark_score": null,
"consensus_score": null
},
"annotations": {
"classifications": [],
"objects": [
{
"bbox": {
"height": 359,
"left": 24,
"top": 85,
"width": 480
},
"color": "#1976d2",
"feature_id": "clvv876mw00023b6qr6mk2o4s",
"instance_uri": "https://api.labelbox.com/masks/feature/clvv876mw00023b6qr6mk2o4s",
"schema_id": "clvcnmppk0aq507z11haa4x66",
"title": "person",
"value": "person"
}
],
"relationships": []
}
}
],
"project_details": {
"ontology_id": "clvcnmpor0aq207z14jesh8w7",
"batch_id": "11EF07D4F9E60FC08820734F207E70B1",
"priority": 5,
"consensus_expected_label_count": {
"Int32": 1,
"Valid": true
}
}
}
},
"media_attributes": {
"asset_type": "image",
"content_length": 122898,
"exif_rotation": 1,
"height": 825,
"mime_type": "image/jpeg",
"sub_type": "jpeg",
"super_type": "image",
"width": 550
}
}
{
"data_row": {
"id": "clvmpaetz76ct0706730rfcq9",
"external_id": null,
"global_key": "TEST-ID-157128604429964085398241585654637789196d",
"row_data": "https://storage.googleapis.com/labelbox-datasets/People_Clothing_Segmentation/jpeg_images/IMAGES/img_0001.jpeg",
"metadata_fields": [],
"details": {
"dataset_id": "clvmpa9et010j0750rlgs8qdi",
"created_at": "2024-04-30T18:07:10.82Z",
"updated_at": "2024-04-30T18:07:14.199Z",
"created_by": null
}
},
"projects": {
"clvo7ajpc014907zxagiw9hry": {
"project_name": "test_lambda",
"labels": [
{
"label_kind": "Default",
"id": "clvo7b6h501ic07fdbg62c32w",
"label_details": {
"created_at": "2024-05-06T17:18:48Z",
"updated_at": "2024-05-06T17:18:48Z",
"created_by": "[email protected]"
},
"performance_details": {
"seconds_to_create": 5759.536,
"seconds_to_review": 0,
"skipped": false,
"benchmark_reference_label": null,
"benchmark_score": null,
"consensus_score": null
},
"annotations": {
"classifications": [],
"objects": [
{
"bbox": {
"height": 359,
"left": 24,
"top": 85,
"width": 480
},
"color": "#1976d2",
"feature_id": "clvv876mw00023b6qr6mk2o4s",
"instance_uri": "https://api.labelbox.com/masks/feature/clvv876mw00023b6qr6mk2o4s",
"schema_id": "clvcnmppk0aq507z11haa4x66",
"title": "person",
"value": "person"
}
],
"relationships": []
}
}
],
"project_details": {
"ontology_id": "clvcnmpor0aq207z14jesh8w7",
"batch_id": "11EF07D4F9E60FC08820734F207E70B1",
"priority": 5,
"consensus_expected_label_count": {
"Int32": 1,
"Valid": true
}
}
}
},
"media_attributes": {
"asset_type": "image",
"content_length": 122898,
"exif_rotation": 1,
"height": 825,
"mime_type": "image/jpeg",
"sub_type": "jpeg",
"super_type": "image",
"width": 550
}
}
{
"data_row": {
"id": "clvmpaetz76cp0706m3jw8ww4",
"external_id": null,
"global_key": "TEST-ID-157128695542350976802229818230180675596d",
"row_data": "https://storage.googleapis.com/labelbox-datasets/People_Clothing_Segmentation/jpeg_images/IMAGES/img_0004.jpeg",
"metadata_fields": [],
"details": {
"dataset_id": "clvmpa9et010j0750rlgs8qdi",
"created_at": "2024-04-30T18:07:10.819Z",
"updated_at": "2024-04-30T18:07:14.196Z",
"created_by": null
}
},
"projects": {
"clvo7ajpc014907zxagiw9hry": {
"project_name": "test_lambda",
"labels": [
{
"label_kind": "Default",
"id": "clvo7b02m01p807znd5bwfr2v",
"label_details": {
"created_at": "2024-05-01T19:19:31Z",
"updated_at": "2024-05-06T17:22:06Z",
"created_by": "[email protected]"
},
"performance_details": {
"seconds_to_create": 3.75,
"seconds_to_review": 0,
"skipped": false,
"benchmark_reference_label": null,
"benchmark_score": null,
"consensus_score": null
},
"annotations": {
"classifications": [],
"objects": [
{
"bbox": {
"height": 540,
"left": 130,
"top": 82,
"width": 311
},
"color": "#1976d2",
"feature_id": "clvo7b6qr00023b6q4xe4nnff",
"instance_uri": "https://api.labelbox.com/masks/feature/clvo7b6qr00023b6q4xe4nnff",
"schema_id": "clvcnmppk0aq507z11haa4x66",
"title": "person",
"value": "person"
}
],
"relationships": []
}
}
],
"project_details": {
"ontology_id": "clvcnmpor0aq207z14jesh8w7",
"batch_id": "11EF07D4F9E60FC08820734F207E70B1",
"priority": 5,
"consensus_expected_label_count": {
"Int32": 1,
"Valid": true
}
}
}
},
"media_attributes": {
"asset_type": "image",
"content_length": 167155,
"exif_rotation": 1,
"height": 825,
"mime_type": "image/jpeg",
"sub_type": "jpeg",
"super_type": "image",
"width": 550
}
}
{
"organizationId": "cloordigw0091073kc6pohyab",
"projectId": "clvo7ajpc014907zxagiw9hry",
"action": "MOVE",
"dataRowIds": ["clvmpaetz76ct0706730rfcq9"],
"originTaskId": "b363420e-1ea9-09dd-914c-d105efc5785a",
"originTaskName": "Initial labeling task",
"destinationTaskId": "c7a8a45d-c37d-4dba-bde7-792581597b10",
"destinationTaskName": "Initial review task",
"actorId": "cloordihe0092073kcorncj7g",
"timestamp": "2024-05-06T17:18:48.797Z"
}
v1
{
"agreement": null,
"benchmarkAgreement": null,
"createdAt": "2023-02-13T18:22:46Z",
"dataRow": {
"createdAt": "2023-01-25T17:54:20Z",
"deletedAt": null,
"externalId": "multi_thread_update_4cfdcb29-be33-4e07-b586-849829385210",
"id": "cldbyu7c7n4jp072scuqt66ll",
"labelCountInProject": 1,
"rowData": "https://labelbox.s3-us-west-2.amazonaws.com/datasets/mapillary_traffic/images/F1xqHzDSi1qP3qSLBXVyJQ.jpg",
"updatedAt": "2023-01-25T20:42:57Z"
},
"dataset": {
"createdAt": "2023-01-25T17:53:58Z",
"deleted": false,
"description": "",
"id": "cldbytq0w2uw8074hgl3hbxr2",
"name": "Datarow Update Test - 5K",
"updatedAt": "2023-01-25T17:54:21Z"
},
"deleted": false,
"id": "cle357uex0g5307z996y28ic0",
"label": "{\"objects\":[{\"featureId\":\"cle357vrp00013b6jsi6dfei0\",\"schemaId\":\"cldan3u0701sc07yi6xyw6up6\",\"color\":\"#ff0000\",\"title\":\"bounding_box\",\"value\":\"bounding_box\",\"bbox\":{\"top\":453,\"left\":951,\"height\":670,\"width\":952},\"instanceURI\":\"https://api.labelbox.com/masks/feature/cle357vrp00013b6jsi6dfei0\"}],\"classifications\":[],\"relationships\":[]}",
"project": {
"createdAt": "2023-02-13T18:20:52Z",
"deleted": false,
"description": "",
"id": "cle355i370h86070hf1v7ata1",
"name": "Webhooks",
"updatedAt": "2023-02-13T18:22:17Z"
},
"secondsToLabel": 0,
"skipped": false,
"updatedAt": "2023-02-13T18:22:46Z",
"user": { "email": "[email protected]", "id": "clbpgxfaehww6076tedo5ejwx" }
}
{
"id": "cl4g555g81ltr07cka1a2b1wv",
"createdAt": "2022-06-16T19:41:49Z",
"updatedAt": "2022-06-16T19:58:25Z",
"secondsToLabel": 6.002,
"label": "{\"objects\":[{\"featureId\":\"cl4hg2phm00013b6j2thsko0u\",\"schemaId\":\"cl2i6hbju1o2y10a321w3cusv\",\"title\":\"grape\",\"value\":\"grape\",\"color\":\"#1CE6FF\",\"bbox\":{\"top\":298,\"left\":445,\"height\":520,\"width\":930},\"instanceURI\":\"https://api.labelbox.com/masks/feature/cl4hg2phm00013b6j2thsko0u\"}],\"classifications\":[],\"relationships\":[]}",
"agreement": null,
"benchmarkAgreement": null,
"deleted": false,
"skipped": false,
"labelCountInProject": 2,
"project": {
"id": "cl2i6gssn1o2210a3gq549fq0",
"createdAt": "2022-04-27T22:57:43Z",
"updatedAt": "2022-06-15T22:19:36Z",
"name": "grapes",
"description": "",
"deleted": false
},
"dataRow": {
"id": "cl2hzfg8g18kw0ztwbtnq0b6q",
"createdAt": "2022-04-27T19:40:43Z",
"updatedAt": "2022-04-27T20:13:51Z",
"deletedAt": null,
"externalId": "CFR_1620.jpg",
"rowData": "https://storage.labelbox.com/ckhmnux5zeutm0825bt0kj34y%2F5375dc13-6442-22d7-f523-e9d252e715ea-CFR_1620.jpg?Expires=1656619105&KeyName=labelbox-assets-key-3&Signature=iOg3tbKe4j7xWZHw1r5rQD4iJIk="
},
"dataset": {
"id": "cl2hz9qay0unl10a3f4wmcsfi",
"createdAt": "2022-04-27T19:36:16Z",
"updatedAt": "2022-04-27T19:40:43Z",
"name": "data",
"description": "",
"deleted": false
},
"user": {
"id": "ckhmnux6hi0p907898jext199",
"email": "[email protected]"
}
}
{
"id": "cl2i6hegh1x660z9i9fzz01a0",
"createdAt": "2022-04-27T22:58:35Z",
"updatedAt": "2022-06-16T20:12:41Z",
"secondsToLabel": 31.448,
"label": "{\"objects\":[{\"featureId\":\"cl2i6hhgd00013f6ghfnrcn6h\",\"schemaId\":\"cl2i6hbju1o2y10a321w3cusv\",\"title\":\"grape\",\"value\":\"grape\",\"color\":\"#1CE6FF\",\"bbox\":{\"top\":701,\"left\":402,\"height\":316,\"width\":238},\"instanceURI\":\"https://api.labelbox.com/masks/feature/cl2i6hhgd00013f6ghfnrcn6h\"},{\"featureId\":\"cl2i6hp3h00033f6gaofl29lx\",\"schemaId\":\"cl2i6hbju1o3010a30ksn2yiy\",\"title\":\"grape mask\",\"value\":\"grape_mask\",\"color\":\"#FF34FF\",\"instanceURI\":\"https://api.labelbox.com/masks/feature/cl2i6hp3h00033f6gaofl29lx\"}],\"classifications\":[],\"relationships\":[]}",
"agreement": null,
"benchmarkAgreement": null,
"deleted": true,
"skipped": false,
"labelCountInProject": 1,
"project": {
"id": "cl2i6gssn1o2210a3gq549fq0",
"createdAt": "2022-04-27T22:57:43Z",
"updatedAt": "2022-06-16T20:06:25Z",
"name": "grapes",
"description": "",
"deleted": false
},
"dataRow": {
"id": "cl2hzfg8g18kk0ztwg22ke4vz",
"createdAt": "2022-04-27T19:40:43Z",
"updatedAt": "2022-04-27T20:13:52Z",
"deletedAt": null,
"externalId": "SYH_2017-04-27_1324.jpg",
"rowData": "https://storage.labelbox.com/ckhmnux5zeutm0825bt0kj34y%2F1d883928-046f-97d5-e3f7-e6c296967c7e-SYH_2017-04-27_1324.jpg?Expires=1656619961&KeyName=labelbox-assets-key-3&Signature=0ma5Yw5qxdJE6_x7ACR7aPEHePM="
},
"dataset": {
"id": "cl2hz9qay0unl10a3f4wmcsfi",
"createdAt": "2022-04-27T19:36:16Z",
"updatedAt": "2022-04-27T19:40:43Z",
"name": "data",
"description": "",
"deleted": false
},
"user": {
"id": "ckhmnux6hi0p907898jext199",
"email": "[email protected]"
}
}
{
"organizationId": "cl81imdtk0cie0yx4g39i0du8",
"projectId": "clbxofp0h05d2074475g14f9v",
"action": "APPROVE",
"dataRowIds": ["cl9elx44f001i076k2pz84tw6"],
"originTaskId": "92665e1f-a507-4308-bdfc-d6be3c629ec7",
"originTaskName": "My custom review step",
"destinationTaskId": null,
"destinationTaskName": "Done",
"actorId": "cl81imdtx0cif0yx4ak0ndq8a",
"timestamp": "2023-01-20T12:06:01.421Z"
}