The Stagewerk API gives an organiser's own systems read access to the data on its public event pages: events, classes, the entry list, the itinerary, bulletins and published results. It reads data, and the one thing it writes is webhook subscriptions (for tools like Zapier and Make, see Connect Stagewerk below). Everything is JSON over HTTPS under /api/v1.
The examples use two shell variables. STAGEWERK_URL is the address of the Stagewerk site you sign in to, for example https://your-stagewerk-host, and STAGEWERK_KEY is your API key.
Get an API key
Keys belong to an organiser, not to a person. The organiser's owner opens API keys in the console (/t/{organiser}/api-keys), gives the key a name that says what uses it, and creates it. The key is shown once, in a box at the top of the page. Copy it into your secret store straight away: Stagewerk keeps only a hash and cannot show it again. Create one key per integration so you can revoke one without touching the others. The page lists each key with the time it was last used, and Revoke stops a key working immediately.
A key can read that organiser's data and nothing else. It cannot read another organiser, and it cannot change anything. One extra can be ticked when you create a key: "Allow this key to subscribe and unsubscribe webhooks". Only an owner who may manage webhooks can tick it. A key with it can add webhook endpoints, which receive entry and team membership events as they happen whether or not the entry list is published (names and states, no contact details), and can remove the endpoints it added itself, but not others. Tick it only for a tool that needs it.
Authenticate
Send the key as a bearer token on every request.
curl -H "Authorization: Bearer $STAGEWERK_KEY" -H "Accept: application/json" "$STAGEWERK_URL/api/v1/events"
A missing, wrong or revoked key gets 401. Always send Accept: application/json.
Pagination
List endpoints that can grow (events, entries, bulletins) use cursor pagination. A response looks like this:
{
"data": [ ... ],
"links": { "first": null, "last": null, "prev": null, "next": "https://.../api/v1/events?cursor=eyJ..." },
"meta": { "path": "https://.../api/v1/events", "per_page": 50, "next_cursor": "eyJ...", "prev_cursor": null }
}
Ask for a page size with limit (1 to 100, default 50). To get the next page, request links.next, or repeat the request with cursor set to meta.next_cursor. When meta.next_cursor is null you have everything. Treat cursors as opaque strings. Lists that are always short (classes, itinerary) are returned whole.
Sparse fields
Add fields with a comma-separated list of keys to receive only those keys in each object, for example fields=competition_number,driver_name. An unknown key returns 422 and the message lists the keys that exist. The keys of every object are listed in the reference below.
Rate limits
Each key may make 120 requests a minute. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining. Over the limit you get 429 with a Retry-After header holding the seconds to wait. Back off for that long, then continue.
Errors
Errors are JSON with a message. Validation errors (422) also have an errors object keyed by parameter.
| Status | Meaning |
|---|---|
| 401 | The key is missing, wrong or revoked. |
| 403 | The key may not read, or may not manage webhooks. |
| 404 | No such event, or it is not public, or nothing is published yet (results). |
| 422 | A query parameter is invalid, for example an unknown field or a limit above 100. |
| 429 | Rate limit reached. Wait Retry-After seconds. |
Worked example: an entry list
This builds an entry-list page for one event: find the event, read its classes, then read every entry.
-
List the organiser's events and pick one by its
slug.curl -s -H "Authorization: Bearer $STAGEWERK_KEY" -H "Accept: application/json" \ "$STAGEWERK_URL/api/v1/events?fields=name,slug,starts_on,status"{ "data": [ { "name": "Club Rallysprint", "slug": "club-rallysprint", "starts_on": "2027-03-14", "status": "entries_open" } ], "links": { ... }, "meta": { ... } } -
Read the classes, to show group headings (
codeandnamematchclass_codeandclass_nameon each entry).curl -s -H "Authorization: Bearer $STAGEWERK_KEY" -H "Accept: application/json" \ "$STAGEWERK_URL/api/v1/events/club-rallysprint/classes?fields=code,name,sort_order" -
Read the entry list, 100 at a time.
curl -s -H "Authorization: Bearer $STAGEWERK_KEY" -H "Accept: application/json" \ "$STAGEWERK_URL/api/v1/events/club-rallysprint/entries?limit=100&fields=competition_number,class_name,driver_name,co_driver_name,vehicle_description"{ "data": [ { "competition_number": 7, "class_name": "Club Sprint", "driver_name": "A. Driver", "co_driver_name": null, "vehicle_description": "2004 Subaru Impreza" } ], "links": { "first": null, "last": null, "prev": null, "next": "https://.../entries?limit=100&cursor=eyJ..." }, "meta": { "path": "https://.../entries", "per_page": 100, "next_cursor": "eyJ...", "prev_cursor": null } } -
While
meta.next_cursoris notnull, request the same URL with&cursor=<next_cursor>and appenddata.
The entry list holds the entries that have a place in the event. It shows names, class, vehicle and state. It never includes email addresses, phone numbers, licence numbers or dates of birth.
Connect Stagewerk
The integrations folder of the Stagewerk repository holds connectors built on this API and on webhooks. They are working code that you install yourself; none of them is listed in the Zapier directory, the Make app list or the WordPress.org plugin directory yet.
- Zapier: an app for the Zapier Platform CLI with triggers for entry accepted, entry withdrawn and results published, and searches for events and entries. It needs a Zapier developer account to upload.
integrations/zapier. - Make: a custom app definition with the same three instant triggers and two list modules, to import in Make's app editor.
integrations/make. - WordPress: a plugin with a settings page, shortcodes and a block for the calendar, next event, join button, entry list and standings widgets.
integrations/wordpress/stagewerk.
Zapier and Make turn a trigger on by calling POST /api/v1/webhooks and off by calling DELETE /api/v1/webhooks/{id}, with a key that has the webhooks extra. The request takes url (a public https address), event_types (one or more of entry.accepted, entry.withdrawn, results.published, membership.changed) and an optional description. The response holds the endpoint id and its signing secret, shown once. DELETE works only for endpoints that same key subscribed; any other id is a 404. The endpoint then appears on the organiser's Webhooks page and follows the same rules as any other: signed requests, retries, a limit of 10 endpoints, and pausing after repeated failures.
TypeScript types
resources/js/api/v1.d.ts in the Stagewerk repository holds TypeScript types generated from the OpenAPI document. Generate your own from /developers/openapi.json with npx openapi-typescript.
Reference
The sections below are generated from the same OpenAPI 3.1 document you can download at /developers/openapi.json.
Changelog
- : new
POST /api/v1/webhooksandDELETE /api/v1/webhooks/{endpoint}let a key subscribe and unsubscribe webhooks. They need a key created with "Allow this key to subscribe and unsubscribe webhooks"; existing keys stay read-only. See Connect Stagewerk above for the Zapier, Make and WordPress connectors in the repository. - :
/api/v1/events/{event}/entriesnow returns 404 until the organiser publishes the entry list on the Widgets page. Publish the list there, then the endpoint returns it.
GET/api/v1/events/{event}/bulletins
List the bulletins of an event
Published bulletins the organiser made public, in number order.
| Name | In | Required | Description |
|---|---|---|---|
event | path | yes | The event slug |
limit | query | no | Rows per page, 1 to 100. Default 50. |
cursor | query | no | The `next_cursor` of the previous page. Opaque; do not build one. |
fields | query | no | Comma-separated keys to keep in each returned object, for example `fields=name,slug`. |
| Key | Type |
|---|---|
id | integer |
number | integer or null |
title | string |
body | string |
published_at | string or null |
published_display | string or null |
replaces_number | integer or null |
event | object |
organiser | object or null |
GET/api/v1/events/{event}/entries
List the entry list of an event
The entries that hold a place in the event, in the order they were created (by id); sort by competition_number on your side, since not every entry has a number yet.
Names, team, class, vehicle and state only: no contact details, licence numbers or dates of birth.
Competitors who asked not to be named on public entry lists are left out (a driver loses the whole entry, a co-driver only their name).
Only served once the organiser has published the entry list; until then the response is 404, as on the public Entries tab.
| Name | In | Required | Description |
|---|---|---|---|
event | path | yes | The event slug |
limit | query | no | Rows per page, 1 to 100. Default 50. |
cursor | query | no | The `next_cursor` of the previous page. Opaque; do not build one. |
fields | query | no | Comma-separated keys to keep in each returned object, for example `fields=name,slug`. |
| Key | Type |
|---|---|
id | integer |
competition_number | integer or null |
class_code | string |
class_name | string |
driver_name | string |
co_driver_name | string or null |
entrant_name | string or null |
vehicle_description | string or null |
state | string |
state_label | string |
photo_url | string or null |
GET/api/v1/events
List events
The events of the organiser that owns the key which are visible on its public site, oldest start first.
| Name | In | Required | Description |
|---|---|---|---|
limit | query | no | Rows per page, 1 to 100. Default 50. |
cursor | query | no | The `next_cursor` of the previous page. Opaque; do not build one. |
fields | query | no | Comma-separated keys to keep in each returned object, for example `fields=name,slug`. |
| Key | Type |
|---|---|
name | string |
slug | string |
url | string |
discipline | string |
discipline_label | string |
sanctioning_body | string |
sanctioning_body_label | string |
venue_name | string |
venue_address | string or null |
town | string or null |
state | string or null |
timezone | string |
starts_on | string |
ends_on | string |
permit_number | string or null |
starts_display | string or null |
finishes_display | string or null |
pace_notes | string or null |
pace_notes_label | string or null |
scrutineering_notes | string or null |
starts_at | string or null |
finishes_at | string or null |
sessions | array |
dates_display | string |
entries_open_local | string or null |
entries_open_display | string or null |
entries_close_local | string or null |
entries_close_display | string or null |
status | string |
status_label | string |
description | string or null |
organiser | object |
locations | array |
contacts | array |
documents | array |
cover | object or null |
course | object or null |
distances | object or null |
GET/api/v1/events/{event}
Get an event
| Name | In | Required | Description |
|---|---|---|---|
event | path | yes | The event slug |
limit | query | no | Rows per page, 1 to 100. Default 50. |
cursor | query | no | The `next_cursor` of the previous page. Opaque; do not build one. |
fields | query | no | Comma-separated keys to keep in each returned object, for example `fields=name,slug`. |
GET/api/v1/events/{event}/classes
List the classes of an event
Every class of the event in the organiser's order. Not paginated: an event has a handful of classes.
| Name | In | Required | Description |
|---|---|---|---|
event | path | yes | The event slug |
limit | query | no | Rows per page, 1 to 100. Default 50. |
cursor | query | no | The `next_cursor` of the previous page. Opaque; do not build one. |
fields | query | no | Comma-separated keys to keep in each returned object, for example `fields=name,slug`. |
| Key | Type |
|---|---|
id | integer |
event_id | integer |
name | string |
code | string |
category | string or null |
sort_order | integer |
cap | integer or null |
drivetrain | string |
min_capacity_cc | integer or null |
max_capacity_cc | integer or null |
forced_induction | string |
min_year | integer or null |
max_year | integer or null |
tyre_rule | string or null |
GET/api/v1/events/{event}/itinerary
List the itinerary of an event
Stages, liaisons and service points in running order, with the computed time the first car is due at each.
Not paginated. The totals are in meta.totals, in metres.
| Name | In | Required | Description |
|---|---|---|---|
event | path | yes | The event slug |
limit | query | no | Rows per page, 1 to 100. Default 50. |
cursor | query | no | The `next_cursor` of the previous page. Opaque; do not build one. |
fields | query | no | Comma-separated keys to keep in each returned object, for example `fields=name,slug`. |
| Key | Type |
|---|---|
id | integer |
event_id | integer |
position | integer |
kind | string |
code | string |
name | string |
heat | integer or null |
leg | integer or null |
scored | boolean |
distance_m | integer or null |
distance_label | string |
target_seconds | integer or null |
target_label | string |
fixed_first_car_at | string or null |
fixed_first_car_local | string or null |
notes | string or null |
first_car_due | string or null |
first_car_label | string |
GET/api/v1/events/{event}/notices
List the notice board of an event
Every public post in order of publication: bulletins, documents, classifications and stewards' decisions, each with its number, category and the time it was published in the event's time zone. A corrected post stays, marked superseded, with links to the post that replaced it. Not paginated.
| Name | In | Required | Description |
|---|---|---|---|
event | path | yes | The event slug |
limit | query | no | Rows per page, 1 to 100. Default 50. |
cursor | query | no | The `next_cursor` of the previous page. Opaque; do not build one. |
fields | query | no | Comma-separated keys to keep in each returned object, for example `fields=name,slug`. |
| Key | Type |
|---|---|
number | integer |
category | string |
reference | string |
title | string |
published_at | string |
published_display | string |
timezone | string |
url | string or null |
superseded | boolean |
supersedes | object or null |
superseded_by | object or null |
needs_confirmation | boolean |
imported | boolean |
original_date | string or null |
personal | boolean |
GET/api/v1/events/{event}/results
Get published classifications
The latest published classification of the event, or the one in version (a version number from the
versions list). Only what the organiser has published; unpublished figures are never served. 404 until
something is published.
| Name | In | Required | Description |
|---|---|---|---|
event | path | yes | The event slug |
version | query | no | Classification version number. Default: the latest published version. |
| Key | Type |
|---|---|
event | object |
version | object |
is_latest | boolean |
scopes | array |
stages | array |
show_runs | boolean |
versions | array |
live | object or null |
standings | object or null |
updated_display | string |
GET/api/v1/events/{event}/stage-times/{itineraryItem}
Get the times of one stage
One stage of the itinerary (use the id from the itinerary list), in order of time. Which times are shown,
published or live, follows the event's stage-times setting; 404 when the event shows none.
| Name | In | Required | Description |
|---|---|---|---|
event | path | yes | The event slug |
itineraryItem | path | yes | The itinerary item ID |
version | query | no | Classification version number. Default: the latest published version. |
| Key | Type |
|---|---|
event | object |
stage | array |
rows | array |
previous | object or null |
next | object or null |
stages | array |
source | object |
has_results | boolean |
live | object or null |
updated_display | string |
POST/api/v1/webhooks
Subscribe a webhook
Registers an HTTPS address to receive webhook events, the way the Webhooks page does. Needs a key created with "Allow this key to manage webhook subscriptions"; any other key gets 403.
The response holds id (use it to unsubscribe) and the signing secret, which is shown this once. See the webhook documentation for the event payloads and how to verify them.
DELETE/api/v1/webhooks/{endpoint}
Unsubscribe a webhook
Deletes an endpoint and its delivery log. Needs the same key as subscribing, and only works on endpoints that same key subscribed: any other id, including endpoints made on the Webhooks page, is a 404.
| Name | In | Required | Description |
|---|---|---|---|
endpoint | path | yes | The endpoint ID |