Skip to content

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.

  1. Open the Webhooks settings and enter the HTTPS URL Teleport should call.
  2. 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:

{
  "eid": "hd72kkw82ksa",
  "sid": "3f417d8s82d484236ab911f160c738ebd",
  "state": "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 200 or 204 to 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 eid and state.

💡 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.