Webhooks

Webhook events notify you when demo/highlight processing reaches a terminal state.

Configure webhook destination

Set your callback URL in Rankacy dashboard for the same token/account that uploads demos and queues highlights.

Event delivery requires a configured webhook URL.

Event types

Processing events emitted by the API:

  • demo.processed.success
  • demo.processed.failed
  • highlight.processed.success
  • highlight.processed.failed

When each event is emitted

  • demo.processed.*: when a demo transitions to terminal status (SUCCESS / FAILED)
  • highlight.processed.*: when a highlight transitions to terminal status (SUCCESS / FAILED)

Failed events include data.error.message; data.error.code is optional.

Events

demo.processed.success

{
  "id": "evt_2e4f7a4f-4afb-45a4-9538-0ed84f703db8",
  "type": "demo.processed.success",
  "created_at": "2026-02-12T12:21:39.146102Z",
  "data": {
    "demo_id": 52,
    "status": "success"
  }
}

demo.processed.failed

{
  "id": "evt_4d2f6f89-6f7f-4c5b-9af3-4d9e260fd2da",
  "type": "demo.processed.failed",
  "created_at": "2026-02-12T12:23:01.481032Z",
  "data": {
    "demo_id": 52,
    "status": "failed",
    "error": {
      "message": "Parser crashed",
      "code": "PARSER_ERROR"
    }
  }
}

error.code is optional.

highlight.processed.success

{
  "id": "evt_4f7f546b-d8ef-4761-91dd-d0da6fa6ce7e",
  "type": "highlight.processed.success",
  "created_at": "2026-02-18T11:06:34.145091Z",
  "data": {
    "highlight_id": 311,
    "demo_id": 52,
    "status": "success"
  }
}

highlight.processed.failed

{
  "id": "evt_59f7ff0b-74f2-4b8e-b62e-1f46dcb6a5af",
  "type": "highlight.processed.failed",
  "created_at": "2026-02-18T11:07:01.962932Z",
  "data": {
    "highlight_id": 311,
    "demo_id": 52,
    "status": "failed",
    "error": {
      "message": "Highlight processing failed",
      "code": "RUNTIME_ERROR"
    }
  }
}

error.code is optional.

Payload notes

  • id: stable event identifier (reused across retries for the same event)
  • type: one of the four event types above
  • created_at: UTC timestamp in ISO 8601 format
  • data.demo_id: always present for demo and highlight events
  • data.highlight_id: present only for highlight.processed.*
  • data.status: success or failed
  • data.error: present only on *.failed events

Delivery behavior

  • asynchronous dispatch from worker loop
  • at-least-once delivery
  • retries for non-2xx responses/network errors
  • event enqueue is idempotent per user + resource + event type

Default retry configuration:

  • max attempts: 6
  • schedule (seconds): 30, 120, 300, 900, 1800, 3600
  • additional jitter: up to 10s
  • timeout per attempt: 5s

A 2xx response marks delivery as success.

Request headers

Each webhook POST includes:

  • Content-Type: application/json
  • X-Event-Id: <event_id>
  • X-Event-Type: <event_type>
  • X-Request-Id: <event_id>

Idempotency

Because delivery is at-least-once, deduplicate by event ID.

Recommended key:

  • X-Event-Id header, or
  • JSON body field id

FastAPI receiver example

from fastapi import FastAPI, Request, HTTPException

app = FastAPI()
seen_event_ids = set()  # Replace with Redis/DB in production

@app.post("/rankacy/webhook")
async def rankacy_webhook(request: Request):
    event_id = request.headers.get("X-Event-Id")
    event_type = request.headers.get("X-Event-Type")
    payload = await request.json()

    if not event_id:
        event_id = payload.get("id")
    if not event_id:
        raise HTTPException(status_code=400, detail="Missing event id")

    if event_id in seen_event_ids:
        return {"ok": True, "duplicate": True}

    seen_event_ids.add(event_id)

    # Business logic
    print({"event_id": event_id, "event_type": event_type, "payload": payload})

    return {"ok": True}

Production note:

  • use durable idempotency storage (Redis/DB), not in-memory set

Signature verification

No cryptographic webhook signature header is currently emitted by this API.

Current hardening recommendations:

  • require HTTPS
  • restrict source IP/network where possible
  • use idempotency checks
  • monitor delivery outcomes in your integration logs

Local testing (ngrok)

Run your webhook server locally (for example on localhost:9000) and tunnel it:

ngrok http 9000

Set the generated HTTPS URL in Rankacy webhook settings.

End-to-end FastAPI showcase

For a working integration example (demo upload + highlight queue + webhook receiver), see: