Overview
Everything here belongs to one venue. Its owner (or StrikerLeagues support) turns things on in My venues → your venue → Integrations:
Booking widget
Free times on your own website. No coding: paste one snippet.
Read-only API
Your fields, free times, bookings and blocks, as JSON, with an API key.
Webhooks
A signed POST to your server when a booking is confirmed, requested, declined or cancelled.
Calendar feeds
Bookings in Google, Outlook or Apple Calendar, and busy times from your calendars blocking bookings.
Booking and paying always happen on StrikerLeagues, signed in, so card details never touch your site. The API reads; it doesn’t create or change bookings.
Booking widget
Copy your venue’s snippet from Integrations → Booking widget, or write it yourself with your venue’s page name (the part after /fields/ in its booking link):
<div data-strikerleagues-venue="zilker-park-soccer-fields-austin"></div>
<script src="https://www.strikerleagues.com/widget.js" async></script>The widget shows the next two weeks of free times and resizes itself to fit. Choosing a time opens your StrikerLeagues page in a new tab with that time picked, where the player signs in and pays. Options go on the div:
| Parameter | Type | What it does |
|---|---|---|
| data-theme | "light" | "dark" | Colours to match your site. Light by default. |
| data-field | field id | Show one field only (ids are in the API, or pick it in the Integrations tab). |
| data-days | 7 – 14 | How many days ahead to offer. 14 by default. |
| data-mode | "button" | Just a “Book now” button that opens your booking page. |
| data-label | text | The button’s text, in button mode. |
Site builders that don’t allow scripts can use a plain iframe instead (set the height yourself):
<iframe src="https://www.strikerleagues.com/embed/fields/zilker-park-soccer-fields-austin?theme=dark"
title="Book on StrikerLeagues" loading="lazy"
style="width:100%;max-width:640px;height:560px;border:0;border-radius:16px"></iframe>API keys
The venue’s owner makes keys in Integrations → API & webhooks. A key starts with sl_live_, is shown once, and reads that venue only. Send it in the Authorization header (or X-Api-Key):
curl https://strikerledger-production.up.railway.app/api/v1/open/bookings \
-H "Authorization: Bearer sl_live_…"Keys read bookings and bookers’ names. Use them from your server only, never in a web page or an app people can download. Revoke a key in the Integrations tab if it leaks.
All times are UTC (ISO 8601). Money is in the smallest unit (cents) with its currency. The venue’s time zone is in /venue.
Errors and limits
Every response has the same shape. Errors use the HTTP status plus a code you can check:
{
"success": false,
"error": { "code": "API_KEY_INVALID", "message": "That API key isn’t valid (it may have been revoked)." },
"meta": { "timestamp": "2026-11-06T15:04:53.093Z" }
}| Parameter | Type | What it does |
|---|---|---|
| 401 | API_KEY_MISSING / API_KEY_INVALID | No key, or a revoked or unknown one. |
| 400 | VALIDATION_ERROR | A query parameter is wrong; the message says which. |
| 404 | NOT_FOUND | No such endpoint, or the booking isn’t at your venue. |
| 429 | RATE_LIMITED | More than 120 requests in a minute with one key. Wait Retry-After seconds. |
Each response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (seconds until the minute resets). To keep in sync, prefer webhooks over polling, or poll /bookings?updatedSince=… every few minutes.
Endpoints
Base URL: https://strikerledger-production.up.railway.app/api/v1/open
GET/venue
The venue: name, address, location, time zone, contact details, cancellation policy and its public booking link (bookingUrl).
GET/fields
Every field: surface, indoor, lights, game sizes (5v5 … 11v11), bookingMode (instant or request), weekly opening hours (local time, dayOfWeek 0 = Sunday) and hourly prices.
GET/availability
Free, bookable times, exactly as players see them on your page (opening hours, minus bookings, blocks and busy times from imported calendars).
| Parameter | Type | What it does |
|---|---|---|
| date | YYYY-MM-DD | Required. The venue’s local date. |
| days | 1 – 14 | How many days from date. Default 1. |
| durationMinutes | 30 – 240 | Length of each slot. Default 60. |
| gameFormat | 5v5 … 11v11 | Only fields that take this game size. |
| fieldId | uuid | One field only. |
{
"success": true,
"data": {
"timeZone": "America/Chicago",
"durationMinutes": 60,
"bookingUrl": "https://www.strikerleagues.com/fields/zilker-park-soccer-fields-austin",
"fields": [
{
"id": "1b3bd028-…",
"name": "Field 1",
"gameFormats": ["7v7", "11v11"],
"slots": [
{ "startsAt": "2026-11-16T23:00:00.000Z", "endsAt": "2026-11-17T00:00:00.000Z", "price": { "amount": 9500, "currency": "usd" } }
]
}
]
}
}To send someone straight to a time, link to bookingUrl with ?date=2026-11-16&format=SEVEN_V_SEVEN&minutes=60&field=<id>&start=<startsAt>#book.
GET/bookings
Bookings in start-time order, 50 a page (up to 100). Holds and unfinished checkouts are left out. Page with cursor: pass the meta.nextCursor of the previous page until it’s null.
| Parameter | Type | What it does |
|---|---|---|
| from | ISO date-time | Bookings starting at or after this. Default: now. |
| to | ISO date-time | Bookings starting before this. |
| status | see below | confirmed, requested, declined, cancelled, completed or no_show. |
| fieldId | uuid | One field only. |
| updatedSince | ISO date-time | Only bookings changed since then (handy for polling). |
| limit | 1 – 100 | Page size. Default 50. |
| cursor | string | From meta.nextCursor. |
{
"id": "4034ef5c-5693-402a-ba1b-696611958ec1",
"status": "confirmed",
"field": { "id": "1b3bd028-4027-4e9b-9d4c-3da97d4e31d7", "name": "Field 1" },
"startsAt": "2026-11-18T00:00:00.000Z",
"endsAt": "2026-11-18T01:00:00.000Z",
"gameFormat": "7v7",
"price": { "amount": 12000, "currency": "usd" },
"booker": { "name": "Pat Player" },
"series": null,
"leagueMatchId": null,
"cancelledAt": null,
"cancellationReason": null,
"createdAt": "2026-11-06T15:05:56.000Z",
"updatedAt": "2026-11-06T15:05:56.000Z"
}status: requested is waiting for the venue’s answer (card authorised, not charged); declined was turned down or not answered in 24 hours; cancelled covers cancellations by the player or the venue and refunds. series is set for weekly bookings ({ id, week }); leagueMatchId when a league booked it for a match.
GET/bookings/{id}
One booking, in the shape above. 404 if it isn’t at your venue.
GET/blocks
Time the venue blocked: kind is internal (your own club or league use), closure, or imported_calendar (busy in a calendar you imported). from defaults to now and to to 31 days later (at most 366 days); fieldId filters.
Webhooks
Add up to five https endpoints in Integrations → API & webhooks, choose their events, and use Send test to try one. We POST JSON like this:
StrikerLeagues-Event: booking.confirmed
StrikerLeagues-Delivery: 9d0c3f9e-…
StrikerLeagues-Signature: t=1793977556,v1=5f1c0b…
Content-Type: application/json
{
"id": "evt_9035cf198177d85cfd34b50f",
"type": "booking.confirmed",
"created": "2026-11-06T15:05:56.681Z",
"venueId": "2781d6fe-ac44-4c18-9a39-f1b86f83e1b5",
"data": { "booking": { …the booking, as in the API… } }
}Events
| Parameter | Type | What it does |
|---|---|---|
| booking.confirmed | event | A booking is paid and confirmed, or the venue approved a request. |
| booking.requested | event | Someone asked to book a request-to-book field. Answer within 24 hours on StrikerLeagues. |
| booking.declined | event | A request was declined, or nobody answered in time. |
| booking.cancelled | event | Cancelled by the player or the venue, or a weekly payment failed. |
| webhook.test | event | Sent by the Send test button (data.message only). |
A weekly booking sends one event per week’s booking. Events can arrive out of order, so compare updatedAt if order matters.
Checking signatures
Each endpoint has a signing secret (whsec_…, shown in the Integrations tab). StrikerLeagues-Signature holds a Unix timestamp t and v1, the hex HMAC-SHA256 of "<t>.<raw body>" with your secret. Check it against the raw body, reject old timestamps, and compare in constant time:
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const SECRET = process.env.STRIKERLEAGUES_WEBHOOK_SECRET; // whsec_…
// Use the raw body: re-serialised JSON won't match the signature.
app.post('/strikerleagues/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const header = req.get('StrikerLeagues-Signature') || '';
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const t = Number(parts.t);
const expected = crypto.createHmac('sha256', SECRET).update(`${t}.${req.body}`).digest('hex');
const fresh = Math.abs(Date.now() / 1000 - t) < 300; // 5 minutes
const valid =
fresh && parts.v1?.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
if (!valid) return res.sendStatus(400);
const event = JSON.parse(req.body);
// Events can arrive more than once: skip ids you've already handled.
if (event.type === 'booking.confirmed') {
// event.data.booking …
}
res.sendStatus(200); // answer fast; do slow work afterwards
});Retries
- Answer with any 2xx within 10 seconds. Redirects aren’t followed.
- Otherwise we try again after 1 minute, 5 minutes, 30 minutes, 2, 6, 12 and 24 hours: eight tries in all.
- The same event can arrive more than once. Use its
idto skip repeats. - After 50 failed tries in a row an endpoint is switched off. Fix it, then switch it back on in the Integrations tab.
- The last 25 deliveries per endpoint, with their payloads, are listed under Recent deliveries (kept for 30 days).
Calendar feeds
Integrations → Calendar sync gives each venue a private iCal link (all fields, or one per field) of bookings, requests and blocks. Calendar apps check it every 30 minutes or so. Anyone with the link can read it, so it can be reset there.
The same page imports other calendars (Google, Outlook, iCloud, another booking system) by their iCal link: their events block the fields you choose, re-checked every 30 minutes, 180 days ahead. Imported busy times show in /blocks as imported_calendar.
Help
Stuck, or need something the API doesn’t do yet? Use Help in the app or contact support. By using the API you agree to our Terms, including keeping players’ details private.

