Developer guide on how to set up Labelbox webhooks through Python SDK
See Webhooks guide page for details on how to use webhooks to receive notifications from activities in Labelbox.
Example configurations to receive requests
from flask import Flask, request
import json
import hmac
import hashlib
# 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, 200
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 json
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'^sha256=', '', event['headers']['x-hub-signature-256'])
digest = hmac.new(
key = bytes(secret, 'utf-8'),
msg = event['body'].encode('utf-8'),
digestmod = hashlib.sha256
)
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, 200
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 a Azure Function to recieve 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 "Error", 500, 200
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 "Success"
Create webhook
Webhook v2 setup
You can create a v2 webhook through the Labelbox UI. The v2 webhook mimics the Export v2 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
v1
{'agreement': None,
'benchmarkAgreement': None,
'createdAt': '2023-02-13T18:22:46Z',
'dataRow': {'createdAt': '2023-01-25T17:54:20Z',
'deletedAt': None,
'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"
}
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"
}