Webhooks

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"
}