openapi: 3.1.0 info: title: BALLDONTLIE Webhooks API version: "1.0.0" description: | Manage webhook endpoints for real-time sports event notifications. Webhooks deliver HTTP POST requests to your server when game events occur across NBA, MLB, NHL, NCAAB, NCAAW, ATP Tennis, WTA Tennis, PGA Golf, EPL, La Liga, Serie A, UCL, Bundesliga, Ligue 1, and MLS. Instead of polling the API, register an endpoint URL and subscribe to the event types you care about. ## Authentication All requests require an API key passed via the `Authorization` header: ``` Authorization: YOUR_API_KEY ``` Webhook API access requires an ALL-ACCESS subscription ($499.99/mo). ## Rate Limits 100 requests per minute per user across all webhook endpoints. ## Quick Start 1. Create an endpoint with `POST /webhooks/v1/endpoints` 2. Subscribe to event types (e.g., `nba.game.started`, `mlb.batter.home_run`, `pga.player.hole_completed`, `atp.match.started`, `epl.game.started`) 3. Store the `secret` from the response for signature verification 4. Receive signed HTTP POST payloads at your URL as events occur 5. Monitor delivery health with `GET /webhooks/v1/usage` ## Signature Verification Every webhook delivery includes three headers for verification: - `X-BDL-Webhook-Id` — unique event ID - `X-BDL-Webhook-Timestamp` — Unix timestamp - `X-BDL-Webhook-Signature` — HMAC-SHA256 signature (`v1=`) Verify by computing `HMAC-SHA256(secret, "${timestamp}.${body}")` and comparing. servers: - url: https://api.balldontlie.io description: Production server components: securitySchemes: ApiKeyAuth: type: apiKey in: header name: Authorization description: Your BALLDONTLIE API key schemas: Error: type: object properties: error: type: string description: Error message required: - error WebhookEndpoint: type: object properties: id: type: string format: uuid description: Unique endpoint identifier example: "1ca7b516-fb3d-4e8c-b37f-567db7bd971e" url: type: string format: uri description: URL that receives webhook POST requests example: "https://example.com/webhooks" description: type: string nullable: true description: Human-readable label for this endpoint example: "NBA game alerts" active: type: boolean description: Whether the endpoint is receiving deliveries. Auto-disabled after 2 consecutive exhausted deliveries. example: true event_types: type: array items: type: string description: Event types this endpoint is subscribed to example: ["nba.game.started", "nba.game.ended", "nba.player.scored"] filters: type: object nullable: true description: Optional filters to narrow which events are delivered consecutive_failures: type: integer description: Number of consecutive delivery failures. Resets to 0 on successful delivery. example: 0 disabled_at: type: string format: date-time nullable: true description: When the endpoint was auto-disabled due to consecutive failures created_at: type: string format: date-time example: "2026-02-20T17:53:20.565Z" updated_at: type: string format: date-time example: "2026-02-26T19:23:00.783Z" WebhookEndpointWithSecret: allOf: - $ref: '#/components/schemas/WebhookEndpoint' - type: object properties: secret: type: string description: HMAC-SHA256 signing secret. Only returned on create and rotate-secret. Store securely. example: "whsec_16c53e0118f24e07181c215670ff4274dcac869faeceb568c28d7efe38dab7fc" WebhookEvent: type: object properties: id: type: string format: uuid description: Unique event identifier example: "3cf70202-d3ea-4422-a6f4-2a78eee406a2" type: type: string description: Event type example: "nba.player.scored" sport: type: string enum: [nba, mlb, nhl, ncaab, ncaaw, atp, wta, pga, epl, laliga, seriea, ucl, bundesliga, ligue1, mls] description: Sport this event belongs to example: "nba" game_id: type: integer nullable: true description: Game ID associated with this event example: 1283054 payload: type: object description: Full event payload. Only included when fetching a single delivery. example: game: id: 1283054 play: text: "LeBron James makes 3-point jump shot" type: "MadeFieldGoal" clock: "4:32" wallclock: "2026-02-26T19:23:30Z" period: 2 away_score: 45 home_score: 52 score_value: 3 player: id: 237 name: "LeBron James" team_id: 14 position: "F" event_type: "nba.player.scored" created_at: type: string format: date-time example: "2026-02-26T19:23:30.468Z" WebhookDelivery: type: object properties: id: type: integer description: Delivery identifier example: 97041 event_id: type: string format: uuid description: ID of the event being delivered example: "3cf70202-d3ea-4422-a6f4-2a78eee406a2" endpoint_id: type: string format: uuid description: ID of the endpoint this was delivered to example: "1ca7b516-fb3d-4e8c-b37f-567db7bd971e" status: type: string enum: [pending, delivering, delivered, failed, exhausted] description: | Delivery status: - `pending` — queued for delivery - `delivering` — currently being sent - `delivered` — successfully delivered (2xx response) - `failed` — delivery attempt failed, will retry - `exhausted` — all retry attempts used example: "delivered" attempts: type: integer description: Number of delivery attempts made example: 1 max_attempts: type: integer description: Maximum retry attempts (3 for free, 5 for ALL-ACCESS) example: 5 next_attempt_at: type: string format: date-time description: When the next retry attempt will occur last_response_status: type: integer nullable: true description: HTTP status code from the last delivery attempt example: 200 last_response_body: type: string nullable: true description: Response body from the last delivery attempt (truncated to 1024 chars) last_error: type: string nullable: true description: Error message from the last failed attempt delivered_at: type: string format: date-time nullable: true description: When the event was successfully delivered example: "2026-02-26T19:23:30.776Z" duration_ms: type: integer nullable: true description: Round-trip delivery time in milliseconds example: 28 created_at: type: string format: date-time updated_at: type: string format: date-time event: $ref: '#/components/schemas/WebhookEvent' WebhookEventType: type: object properties: type: type: string description: Event type identifier example: "nba.game.started" description: type: string description: Human-readable description example: "Game begins" sport: type: string enum: [nba, mlb, nhl, ncaab, ncaaw, atp, wta, pga, epl, laliga, seriea, ucl, bundesliga, ligue1, mls] description: Sport this event type belongs to example: "nba" available: type: boolean description: Whether your subscription tier has access to this event type example: true WebhookUsage: type: object properties: deliveries_this_month: type: integer description: Number of deliveries made in the current billing month example: 66925 deliveries_limit: type: integer description: Maximum deliveries allowed per month example: 500000 endpoints_count: type: integer description: Number of active endpoints example: 3 endpoints_limit: type: integer description: Maximum endpoints allowed example: 10 DeliveryPagination: type: object properties: next_cursor: type: integer nullable: true description: Cursor for the next page. Pass as `cursor` query parameter. per_page: type: integer description: Number of results per page example: 25 security: - ApiKeyAuth: [] paths: /webhooks/v1/event-types: get: operationId: listEventTypes summary: List available event types description: | Returns all event types across all sports. The `available` field indicates whether your subscription tier has access to each event type. Free tier has access to `nba.game.started` and `nba.game.ended` only. ALL-ACCESS tier has access to all 130+ event types across NBA, MLB, NHL, NCAAB, NCAAW, ATP Tennis, WTA Tennis, PGA Golf, EPL, La Liga, Serie A, UCL, Bundesliga, Ligue 1, and MLS. tags: - Event Types responses: "200": description: List of event types content: application/json: schema: type: object properties: data: type: array items: $ref: '#/components/schemas/WebhookEventType' "401": description: Invalid or missing API key content: application/json: schema: $ref: '#/components/schemas/Error' /webhooks/v1/endpoints: get: operationId: listEndpoints summary: List your webhook endpoints description: Returns all webhook endpoints for the authenticated user. tags: - Endpoints responses: "200": description: List of endpoints content: application/json: schema: type: object properties: data: type: array items: $ref: '#/components/schemas/WebhookEndpoint' post: operationId: createEndpoint summary: Create a webhook endpoint description: | Create a new webhook endpoint. The response includes a `secret` field — store it securely for signature verification. The secret is only returned on creation and when rotated. tags: - Endpoints requestBody: required: true content: application/json: schema: type: object required: - url - event_types properties: url: type: string format: uri description: HTTPS URL that will receive webhook POST requests example: "https://example.com/webhooks/bdl" description: type: string description: Human-readable label example: "NBA live game alerts" event_types: type: array items: type: string description: Event types to subscribe to. Use `GET /webhooks/v1/event-types` to see available types. example: ["nba.game.started", "nba.game.ended", "nba.player.scored"] filters: type: object nullable: true description: Optional filters to narrow event delivery responses: "201": description: Endpoint created successfully. The `secret` is included in this response only. content: application/json: schema: type: object properties: data: $ref: '#/components/schemas/WebhookEndpointWithSecret' "400": description: Invalid request (bad URL, missing event_types, invalid event type) content: application/json: schema: $ref: '#/components/schemas/Error' "403": description: Endpoint limit reached or event type requires ALL-ACCESS content: application/json: schema: $ref: '#/components/schemas/Error' /webhooks/v1/endpoints/{endpoint_id}: parameters: - name: endpoint_id in: path required: true schema: type: string format: uuid description: Endpoint UUID get: operationId: getEndpoint summary: Get endpoint details description: Returns a single endpoint. Does not include the signing secret. tags: - Endpoints responses: "200": description: Endpoint details content: application/json: schema: type: object properties: data: $ref: '#/components/schemas/WebhookEndpoint' "404": description: Endpoint not found content: application/json: schema: $ref: '#/components/schemas/Error' patch: operationId: updateEndpoint summary: Update a webhook endpoint description: | Update endpoint properties. All fields are optional — only provided fields are changed. To re-enable a disabled endpoint, set `active: true`. tags: - Endpoints requestBody: required: true content: application/json: schema: type: object properties: url: type: string format: uri description: New URL for the endpoint description: type: string description: New description active: type: boolean description: Enable or disable the endpoint event_types: type: array items: type: string description: Replace subscribed event types filters: type: object nullable: true description: Replace event filters responses: "200": description: Updated endpoint content: application/json: schema: type: object properties: data: $ref: '#/components/schemas/WebhookEndpoint' "400": description: Invalid request content: application/json: schema: $ref: '#/components/schemas/Error' "404": description: Endpoint not found content: application/json: schema: $ref: '#/components/schemas/Error' delete: operationId: deleteEndpoint summary: Delete a webhook endpoint description: Permanently deletes the endpoint and all associated delivery records. tags: - Endpoints responses: "200": description: Endpoint deleted content: application/json: schema: type: object properties: deleted: type: boolean example: true "404": description: Endpoint not found content: application/json: schema: $ref: '#/components/schemas/Error' /webhooks/v1/endpoints/{endpoint_id}/rotate-secret: parameters: - name: endpoint_id in: path required: true schema: type: string format: uuid description: Endpoint UUID post: operationId: rotateEndpointSecret summary: Rotate signing secret description: | Regenerate the HMAC-SHA256 signing secret. The old secret is immediately invalidated. Update your verification code before the next delivery arrives. tags: - Endpoints responses: "200": description: Endpoint with new secret content: application/json: schema: type: object properties: data: $ref: '#/components/schemas/WebhookEndpointWithSecret' "404": description: Endpoint not found content: application/json: schema: $ref: '#/components/schemas/Error' /webhooks/v1/endpoints/{endpoint_id}/test: parameters: - name: endpoint_id in: path required: true schema: type: string format: uuid description: Endpoint UUID post: operationId: testEndpoint summary: Send a test event description: | Sends a signed test event to the endpoint URL. Use this to verify your server is correctly receiving and verifying webhook payloads. The test payload has `type: "test"` and `sport: "test"`. tags: - Endpoints responses: "200": description: Test result content: application/json: schema: type: object properties: success: type: boolean description: Whether the endpoint responded with a 2xx status example: true status: type: integer description: HTTP status code from the endpoint example: 200 error: type: string description: Error message if the request failed (timeout, connection refused, etc.) "404": description: Endpoint not found content: application/json: schema: $ref: '#/components/schemas/Error' /webhooks/v1/endpoints/{endpoint_id}/deliveries: parameters: - name: endpoint_id in: path required: true schema: type: string format: uuid description: Endpoint UUID get: operationId: listDeliveries summary: List deliveries for an endpoint description: | Returns paginated delivery records for an endpoint, ordered by most recent first. Use cursor-based pagination to page through results. tags: - Deliveries parameters: - name: cursor in: query schema: type: integer description: Cursor from `meta.next_cursor` for pagination - name: per_page in: query schema: type: integer minimum: 1 maximum: 100 default: 25 description: Number of results per page (max 100) - name: status in: query schema: type: string enum: [pending, delivering, delivered, failed, exhausted] description: Filter deliveries by status responses: "200": description: Paginated list of deliveries content: application/json: schema: type: object properties: data: type: array items: $ref: '#/components/schemas/WebhookDelivery' meta: $ref: '#/components/schemas/DeliveryPagination' "404": description: Endpoint not found content: application/json: schema: $ref: '#/components/schemas/Error' /webhooks/v1/deliveries/{delivery_id}: parameters: - name: delivery_id in: path required: true schema: type: integer description: Delivery ID get: operationId: getDelivery summary: Get delivery details description: | Returns a single delivery record with the full event payload included. Use this to inspect what was delivered and debug failed deliveries. tags: - Deliveries responses: "200": description: Delivery with full event payload content: application/json: schema: type: object properties: data: $ref: '#/components/schemas/WebhookDelivery' "404": description: Delivery not found content: application/json: schema: $ref: '#/components/schemas/Error' /webhooks/v1/deliveries/{delivery_id}/retry: parameters: - name: delivery_id in: path required: true schema: type: integer description: Delivery ID post: operationId: retryDelivery summary: Retry a delivery description: | Manually retry a failed or exhausted delivery. Resets attempts to 0 and sets status back to `pending` for immediate redelivery. Requires ALL-ACCESS subscription. tags: - Deliveries responses: "200": description: Delivery reset to pending content: application/json: schema: type: object properties: data: $ref: '#/components/schemas/WebhookDelivery' "403": description: Manual retry requires ALL-ACCESS content: application/json: schema: $ref: '#/components/schemas/Error' "404": description: Delivery not found content: application/json: schema: $ref: '#/components/schemas/Error' /webhooks/v1/usage: get: operationId: getUsage summary: Get usage statistics description: | Returns current month delivery count, delivery limit, endpoint count, and endpoint limit for the authenticated user. tags: - Usage responses: "200": description: Usage statistics content: application/json: schema: type: object properties: data: $ref: '#/components/schemas/WebhookUsage' tags: - name: Event Types description: Browse available webhook event types across all sports - name: Endpoints description: Create, update, and manage webhook endpoints - name: Deliveries description: Monitor and retry webhook deliveries - name: Usage description: Track monthly usage and limits