CTFFactory Docs

Webhooks

CTFFactory webhooks allow your systems to receive real-time notifications when important events occur on the platform. Instead of polling the API repeatedly, you register an HTTPS endpoint and CTFFactory sends a JSON POST request to it whenever a subscribed event fires.


Registering a Webhook Endpoint

  1. Navigate to Workspace Settings > Integrations > Webhooks.
  2. Click Add Endpoint.
  3. Enter your endpoint URL (must be HTTPS; HTTP endpoints are rejected).
  4. Select the events you want to receive (see the Event Types table below).
  5. Click Save. CTFFactory generates a signing secret and displays it once β€” copy it immediately and store it securely.

You can also register endpoints via the API:

POST /api/v1/webhooks
Authorization: Bearer ctff_...
Content-Type: application/json

{
  "url": "https://your-app.example.com/hooks/ctffactory",
  "events": ["ctf.deployed", "challenge.generated"],
  "description": "Production event handler"
}

Event Types

Event Trigger
ctf.deployed A CTF instance has been successfully deployed and is accepting traffic
ctf.stopped A CTF instance has been stopped and its infrastructure torn down
ctf.expired A CTF instance has passed its scheduled end time and been automatically shut down
challenge.generated An AI-generated challenge has been created (from the organizer flow or a learning card lab)

More event types may be added over time. Subscribe only to the events relevant to your integration.


Payload Structure

Each webhook delivery is a JSON POST request to your endpoint. All events share a common envelope structure:

{
  "event": "ctf.deployed",
  "id": "evt_01JXABCDE12345",
  "created_at": "2026-05-18T14:32:00Z",
  "workspace_id": "ws_01JXABC123",
  "data": { }
}

The data field contains event-specific payload. Examples:

ctf.deployed

{
  "data": {
    "ctf_id": "ctf_01JXABC456",
    "name": "Spring Boot Camp CTF",
    "url": "https://ctf.yourcompany.com/spring-boot-camp/",
    "deployed_at": "2026-05-18T14:32:00Z",
    "deployed_by": "user_01JXABC789"
  }
}

challenge.generated

{
  "data": {
    "ctf_id": "ctf_01JXABC456",
    "challenge_id": "chal_01JXABC999",
    "title": "Token Forge",
    "category": "web",
    "difficulty": 7,
    "deployable": true,
    "generated_by": "user_01JXABC789"
  }
}

Verifying the HMAC-SHA256 Signature

Every webhook delivery includes an X-CTFFactory-Signature header containing an HMAC-SHA256 digest of the raw request body, signed with the endpoint's signing secret. Always verify this signature before processing the payload to ensure the request genuinely originated from CTFFactory and has not been tampered with.

The signature format is:

X-CTFFactory-Signature: sha256=<hex_digest>

Python Verification Example

import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)

WEBHOOK_SECRET = "your_signing_secret_here"  # Retrieved when you registered the endpoint

@app.route("/hooks/ctffactory", methods=["POST"])
def handle_webhook():
    signature_header = request.headers.get("X-CTFFactory-Signature", "")

    if not signature_header.startswith("sha256="):
        abort(400, "Missing or malformed signature header")

    received_sig = signature_header[len("sha256="):]

    # Compute the expected signature using the raw request body
    expected_sig = hmac.new(
        key=WEBHOOK_SECRET.encode("utf-8"),
        msg=request.get_data(),  # Raw bytes, not parsed JSON
        digestmod=hashlib.sha256
    ).hexdigest()

    # Use hmac.compare_digest to prevent timing attacks
    if not hmac.compare_digest(expected_sig, received_sig):
        abort(401, "Signature verification failed")

    event = request.json
    print(f"Received event: {event['event']} (id={event['id']})")

    # Handle specific events
    if event["event"] == "ctf.deployed":
        handle_ctf_deployed(event["data"])

    return "", 200  # Acknowledge receipt

def handle_ctf_deployed(data):
    print(f"CTF deployed: {data['name']} at {data['url']}")

Important: Always compute the HMAC over the raw request body bytes, not over a re-serialized version of the parsed JSON. JSON serialization is not deterministic and will produce a different digest.


Delivery and Retries

CTFFactory expects your endpoint to return a 2xx HTTP status code within 10 seconds to acknowledge receipt. If the endpoint times out or returns a non-2xx response, CTFFactory retries the delivery with exponential backoff:

Attempt Delay
1st retry 30 seconds
2nd retry 5 minutes
3rd retry 30 minutes
4th retry 2 hours

After 4 failed retries, the delivery is marked as failed and no further attempts are made. Failed deliveries are visible in the Webhook Delivery Log under Workspace Settings. You can manually replay any delivery from the log.


Testing Your Endpoint

Use the Send Test Event button in the Webhooks settings page, or the API endpoint POST /api/v1/webhooks/{webhook_id}/test, to send a synthetic event payload to your endpoint. This is useful for verifying your signature verification code before relying on it in production.

πŸ‡¨πŸ‡¦ Data hosted in Canada Β· Β© 2026 ExamBoot Β· Terms Β· Privacy