Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.bytestack.com/llms.txt

Use this file to discover all available pages before exploring further.

Webhooks let you react to ByteStack job completions without polling the API. When a scheduled job finishes a run, ByteStack sends a signed HTTP POST request to the URL you registered. Your server receives the payload, verifies the signature, and processes the results in real time.

How webhooks work

1

Register a webhook endpoint

Provide ByteStack with the HTTPS URL of your server endpoint. You can do this in the dashboard under Settings → Webhooks, or by calling the API directly.
2

Attach the webhook to a job

When creating a job, set the webhook_url field. ByteStack calls your endpoint after every run of that job.
3

Receive and verify the payload

ByteStack POSTs a JSON payload to your endpoint and signs the request with an HMAC-SHA256 signature in the X-ByteStack-Signature header. Verify it before processing.
4

Respond with a 2xx status

Return any 2xx HTTP status within 10 seconds to acknowledge receipt. If ByteStack does not receive a 2xx, it retries the delivery.

Registering a webhook

Via the dashboard: Go to Settings → Webhooks and click Add endpoint. Enter your HTTPS URL and select the events to subscribe to. Via the API:
curl --request POST \
  --url https://api.bytestack.dev/v1/webhooks \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "url": "https://yourapp.example.com/webhooks/bytestack",
    "events": ["job.completed"]
  }'
{
  "id": "wh_01hx9jk2p4n5m6r7s8t9u0v1w",
  "url": "https://yourapp.example.com/webhooks/bytestack",
  "events": ["job.completed"],
  "created_at": "2026-04-30T14:10:00Z"
}
Copy the webhook secret shown once at creation time and store it securely. You will use it to verify signatures. If you lose it, rotate it from Settings → Webhooks.

Webhook payload format

ByteStack sends a JSON body with the following fields on every job.completed event:
{
  "event": "job.completed",
  "job_id": "job_01hx9jk2p4n5m6r7s8t9u0v1w",
  "run_id": "run_01hx9jk2p4n5m6r7s8t9u0v1x",
  "status": "completed",
  "result_url": "https://api.bytestack.dev/v1/storage/job_01hx9jk2p4n5m6r7s8t9u0v1w%2F2026-04-30T08%3A00%3A00Z.json",
  "timestamp": "2026-04-30T08:01:22Z"
}
FieldTypeDescription
eventstringAlways job.completed for job completion events.
job_idstringThe ID of the job that ran.
run_idstringA unique ID for this specific run.
statusstringcompleted or failed.
result_urlstringURL to download the full result JSON.
timestampstringISO 8601 time the run finished.

Verifying webhook signatures

Always verify the X-ByteStack-Signature header before processing a webhook payload. Without verification, anyone can send fake payloads to your endpoint.
ByteStack signs each request using HMAC-SHA256 with your webhook secret. The signature is sent in the X-ByteStack-Signature header in the format sha256=<hex_digest>. To verify: compute the HMAC-SHA256 of the raw request body using your webhook secret, then compare the result to the value in the header using a constant-time comparison.
import hashlib
import hmac
from flask import Flask, request, abort

app = Flask(__name__)

BITKIT_WEBHOOK_SECRET = "your_webhook_secret_here"


def verify_signature(payload: bytes, signature_header: str) -> bool:
    expected = hmac.new(
        BITKIT_WEBHOOK_SECRET.encode(),
        payload,
        hashlib.sha256,
    ).hexdigest()
    expected_header = f"sha256={expected}"
    return hmac.compare_digest(expected_header, signature_header)


@app.route("/webhooks/bytestack", methods=["POST"])
def handle_webhook():
    signature = request.headers.get("X-ByteStack-Signature", "")
    payload = request.get_data()

    if not verify_signature(payload, signature):
        abort(400, "Invalid signature")

    event = request.json
    job_id = event["job_id"]
    status = event["status"]
    result_url = event.get("result_url")

    print(f"Job {job_id} finished with status: {status}")
    if result_url:
        print(f"Download results from: {result_url}")

    return "", 200

Responding to webhooks

Your endpoint must return a 2xx HTTP response within 10 seconds of receiving the request. ByteStack considers any other status code, a timeout, or a network error as a failed delivery.
If your processing logic takes longer than 10 seconds, acknowledge the webhook immediately by returning 200 OK, then handle the result asynchronously in a background job or queue.

Retry behavior

When a delivery fails, ByteStack retries up to 3 times using exponential backoff:
AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
After 3 failed attempts, the delivery is marked as failed. You can view delivery history and manually retry from Settings → Webhooks → Delivery logs in the dashboard.