Webhooks
Webhooks let your systems react to capture processing without polling the API. When a capture finishes reconstruction - or fails - Teleport sends an HTTP POST request to a URL you control.
A webhook is delivered for two events:
- The capture finishes processing and is ready to view and download.
- Processing failed and the capture will not become ready.
1. Configure Your Webhook#
Webhooks are configured per organisation in your Developer Dashboard.
- Open the Webhooks settings and enter the HTTPS URL Teleport should call.
- A signing secret is generated automatically. Copy it - you'll use it to verify the signature of incoming requests.
One webhook URL per organisation
Each organisation has a single webhook URL. Updating the URL keeps the same signing secret. Webhooks fire for every capture in your organisation.
2. Webhook Payload#
Teleport sends a POST request with a JSON body and the following headers:
| Header | Description |
|---|---|
Content-Type |
Always application/json. |
X-Teleport-Signature |
HMAC-SHA256 signature of the raw request body. See Verify the Signature. |
Body fields:
| Field | Type | Description |
|---|---|---|
eid |
string | The capture identifier. |
sid |
string | The capture share identifier, used to fetch capture metadata. |
state |
string | READY or ERROR. |
error_reason |
string | Only present when state is ERROR. See the table below. |
Example - capture ready:
Example - capture failed:
{
"eid": "hd72kkw82ksa",
"sid": "3f417d8s82d484236ab911f160c738ebd",
"state": "ERROR",
"error_reason": "NOT_ENOUGH_FRAMES"
}
Error Reasons#
When state is ERROR, error_reason is one of the following stable slugs:
| Error reason | Cause |
|---|---|
NOT_ENOUGH_FRAMES |
The capture did not contain enough usable frames. |
INPUT_VALIDATION_FAILED |
The uploaded data could not be validated. |
CONVERSION_FAILED |
The input could not be converted to our common format. |
POSE_REFINEMENT_FAILED |
Camera poses could not be recovered from the input. |
DENSIFICATION_FAILED |
An internal processing error occurred during densification. |
TRAINING_FAILED |
An internal processing error occurred during training. |
EXPORT_FAILED |
An internal processing error occurred during export. |
💡 The first four reasons are typically caused by the input and may be resolved by re-capturing or re-uploading. The remaining reasons indicate a transient error on our side.
3. Verify the Signature#
Every request carries an X-Teleport-Signature header so you can confirm it genuinely came from Teleport. The signature is the HMAC-SHA256 of the raw request body, keyed with your signing secret, encoded as a hexadecimal string.
To verify a request, compute the same HMAC over the raw bytes you received and compare it to the header value.
import hashlib
import hmac
def is_valid_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode('utf-8'),
msg = payload,
digestmod = hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature)
⚠️ Compute the HMAC over the exact bytes of the request body, before any JSON parsing or re-serialization. Re-encoding the JSON can change whitespace or key order and will produce a different signature.
4. Responding and Retries#
- Respond with HTTP
200or204to acknowledge receipt. Any other status, a timeout, or a connection error is treated as a failure. - Respond quickly - requests time out after a few seconds. Do any heavy work asynchronously after acknowledging.
- Delivery is at least once. Failed deliveries are retried, so the same event may arrive more than once. Make your handler idempotent - for example, key your processing on the combination of
eidandstate.
💡 You can review recent webhook deliveries, including response codes, in the Webhooks section of your Developer Dashboard.
What Next?#
When you receive a READY event, use the capture's sid to fetch model URLs and poses from the Capture Metadata endpoint.