Webhooks are currently the best and fastest way to access classification results for requests sent to Unitary's API - they allow you to act on the results as soon as they are available.
Whenever a job finishes processing, a POST request will be sent to the callback_url parameter provided in the body of the API request.
For easy experimentation, several free services can help you debug webhooks easily. Some of these include: RequestBin, Webhook Inbox and InspectBin.
Example: A simple webhook receiver (Python)
The following Python (FastAPI) server will listen for results and log the jobs that have at least one policy category detected.
from fastapi import Request, FastAPIapp =FastAPI()@app.post("/results")asyncdefget_body(request: Request): result =await request.json()if result["is_error"]isFalse: policy_categories = result["result"].get("policy_categories", [])iflen(policy_categories)>0:print("Policy category detected")return"OK"
Response Validation
The response payload is validated using the X-Hub-Signature-256 header which requires base64 formatting.
The payload is hashed using the HMAC-SHA256 algorithm with a secret key (or cryptographic key) that is set up following the instructions atSetting up a secret key. If no key is set, the header in question will not be present.
For the error JSON presented above and the secret set as "test_key", the header will have the value "hYOb+67Z15TMwHO678Yd2fQykmBDwPdBa+O7FnXOqOY=".
This online generator uses the same algorithm. Please be aware of the whitespaces inside the payload.
Example: Validating webhooks (Python)
In the following example, the payload is validated according to the secret key that has previously been set up.
from fastapi import Header, Request, FastAPIimport base64import hashlibimport hmacSECRET_KEY ="test_key"app =FastAPI()defcompute_signature(payload:str,secret_key:str) ->str: digest = hmac.new( secret_key.encode(), msg=payload.encode("utf-8"), digestmod=hashlib.sha256 ).digest() signature = base64.b64encode(digest).decode()return signature@app.post("/results")asyncdefget_body(request: Request,x_hub_signature_256:str|None=Header(default=None)): result =await request.body()ifcompute_signature(result.decode(), SECRET_KEY)== x_hub_signature_256:print("Body validated")# .. do processingelse:print("Body not validated")return"OK"
Setting up a secret key
You will receive a secret key from Unitary when you get your API Key.
If you still don't have a key or need a new one, please write to support@unitary.ai
Webhook request payloads
{"data": {"job_id":"{JOB_ID}","callback_url":"{CALLBACK_URL}","is_error":false,"result": {"policy_categories": [ {"name":"ARMS_AMMO","description":"Arms & Ammunition","risk_level":"HIGH" }, {"name":"DEATH_INJURY_MILITARY","description":"Death, Injury or Military Conflict","risk_level":"MEDIUM" } ],"metadata": {"width":720,"height":480,"fps":30,"duration":11.6,"seconds_processed":11 },"url":"{RESOURCE_URL}" } }}
{"data": {"job_id":"{JOB_ID}","callback_url":"{CALLBACK_URL}","is_error":true,"result": {"error":"Failed to decode video." } }}
Custom payload data
The callback_url is used as is, so you can append custom data to the request by controlling the query parameters.
For example, you can send back the original file name or the time of the original request.
// with filename"callback_url": "your.callback.server/?filename=original_file_name.mp4"// with timestamp"callback_url": "your.callback.server/?current_timestamp=1687864622"// with filename and timestamp"callback_url": "your.callback.server/?current_timestamp=1687864622&filename=original_file_name.mp4"
The additional data will also be part of the payload body.