Agent skill

Live Tennis API Webhooks Skill

Receive and verify Live Tennis API webhooks. Use when setting up Live Tennis API webhook handlers, debugging X-LTAPI-Signature HMAC-SHA256 verification, registering a webhook with POST /webhooks, or handling live tennis frames like score, point, break_point, and break_point_result.

Install this skill

npx skills add hookdeck/webhook-skills --skill live-tennis-api-webhooks


When to Use This Skill

  • How do I receive Live Tennis API webhooks (live scores, points, break points)?
  • How do I verify the X-LTAPI-Signature header?
  • How do I register a webhook with POST /webhooks and keep the signing secret?
  • How do I handle score, point, break_point or break_point_result frames?
  • Why is my Live Tennis API webhook signature verification failing?

How Live Tennis API Delivers Webhooks

You register an HTTPS URL with POST /webhooks (ULTRA plan, direct API key only, max 3 webhooks per key). The 201 response contains a secret that is shown exactly once: store it as LIVE_TENNIS_API_WEBHOOK_SECRET. Each delivery is one POST of the same frame the WebSocket feed sends. Delivery is best-effort and at-most-once: one attempt, ~3 second timeout, redirects disabled, and the webhook is disabled after 25 consecutive failures.

Verification (core)

Every request carries X-LTAPI-Signature: sha256=<hex>, the hex HMAC-SHA256 of the raw request body keyed with your webhook secret. Require the sha256= prefix and strip it. X-LTAPI-Timestamp (Unix seconds) is sent but is not signed, so don't build a replay check on it. There is no webhook SDK, so verify manually against the raw body (don't parse JSON first).

const crypto = require('crypto');

function verifyLiveTennisSignature(rawBody, header, secret) {
  const match = /^sha256=([0-9a-f]{64})$/i.exec(typeof header === 'string' ? header : '');
  if (!match) return false; // validate hex before Buffer.from(x, 'hex'), which silently truncates
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest();
  return crypto.timingSafeEqual(Buffer.from(match[1], 'hex'), expected);
}
import hashlib, hmac

def verify_live_tennis_signature(raw_body: bytes, header: str, secret: str) -> bool:
    if not header.startswith("sha256="):
        return False
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    # compare bytes so a non-ASCII header fails verification instead of raising
    return hmac.compare_digest(header[len("sha256="):].lower().encode(), expected.encode())

For complete handlers with route wiring, event dispatch, and tests, see:

Event Types (X-LTAPI-Event)

The X-LTAPI-Event header names the frame type; the JSON body normally carries the same value in type, and the examples dispatch on the (signed) body type, falling back to the header.

EventDescription
scoreComplete current score after every accepted live-score commit (a missed one self-corrects on the next)
pointOne played point, with seq, winner, and the score after the point (an event, not a state)
break_pointA break point just arose (server, returner, break_points, win_probability_p1)
break_point_resultA break point resolved (outcome is held or broken)

For payload details, see references/overview.md.

Important Headers

HeaderDescription
X-LTAPI-Signaturesha256=<hex> HMAC-SHA256 of the raw body
X-LTAPI-EventFrame type (score, point, break_point, break_point_result)
X-LTAPI-TimestampUnix seconds at send time. Not signed: treat as informational

Environment Variables

LIVE_TENNIS_API_WEBHOOK_SECRET=your_webhook_secret   # `secret` from the POST /webhooks 201 response

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 live-tennis-api --path /webhooks/live-tennis-api

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 9, 2026

View on GitHub →