API Reference Changelog

Let's Do This API — Changelog (0.1.0)

Download OpenAPI specification:

Consumer-visible changes to the public API.

August 2026

Timing ingest endpoint published

2026-08-20

POST /timing-inbound/v1 is now documented under the new Timing section. Timing providers enabled for the integration push data (currently bib numbers) back for startlist entries they received from Let's Do This, as a single message or a batch of up to 500.

The endpoint itself is not new — it was previously undocumented while the contract was agreed directly with the first partner. It is available to select timing partners only; other callers receive the standard 401.


Import endpoints you are not enabled for now answer 403

2026-08-10

Every import surface — bookings, line items, add-on products, discount codes, credits, referrals, and reserved entry participants — previously answered 404 when your organization was not enabled for it. They now answer 403 with a message naming the surface:

Booking imports are not enabled for this organizer. Ask your Lets Do This contact to enable them.

The 404 said the endpoint did not exist, which was never true — it is documented here — and left no way to tell "not switched on yet" from a wrong URL. If you branch on the status code to detect that your organization lacks access, match 403 rather than 404.

Job IDs are unaffected: a status endpoint still answers 404 for a job that does not exist or belongs to another organization, so it cannot be used to discover other organizations' jobs.


A permanent import failure is no longer reported as INTERNAL

2026-08-07

A reserved entry participant assignment the import cannot use — one naming a blank identifier, for instance — now fails with the code INVALID_ASSIGNMENT in the job status errors. It previously reported INTERNAL.

The distinction matters for retries. INTERNAL means the cause could not be named, so trying again is reasonable. INVALID_ASSIGNMENT means the assignment itself was rejected before anything was written, so resubmitting the same row cannot succeed — correct the row and submit it again.

Other codes describe the state that blocked the assignment rather than the assignment itself, and that state can change: NO_CAPACITY_REMAINING clears when a place frees or the group's capacity is raised.

code is a free-form string, so no schema change is involved and existing handling continues to work.


Read a booking's add-on allocations

2026-08-05

Experimental: This endpoint is experimental and may change before general availability.

GET /v0/bookings/{id}/add-on-allocations returns a cursor-paginated list of the participant add-on allocations stored for a booking. Each allocation includes its participant and purchase line-item references, add-on and variant details, operational and financial status, and creation and update timestamps. Imported records also include external add-on, variant, and line-item identifiers when available.


July 2026

Delete an imported booking

2026-07-27

DELETE /v0/bookings/{id} retires a booking you imported through the Public API, so you can correct a bad row and import it again under the same externalBookingId.

The booking is not removed: it moves to CANCELLED and stays readable. The external ID it was imported under is freed at the same time, so re-importing that ID creates a new booking rather than reporting alreadyImported. Deleting is idempotent — deleting an already-deleted booking succeeds and changes nothing.

Only bookings whose source is API can be deleted. Anything else returns 403, and a booking that does not exist or belongs to another organizer returns 404.


Breaking: the external-booking-ID lookup returns a list

2026-07-27

GET /v0/bookings/external/{externalBookingId} now returns a PagedBookingSummaries envelope instead of a single Booking, and an ID that matches nothing returns an empty list with 200 rather than 404.

One external ID can legitimately match more than one booking — correcting an import cancels the original and re-imports it, so both survive under the ID you supplied. The previous shape had to choose one on your behalf and could not show you the other. Results are oldest first, matching the other list endpoints, so the most recent match is last. The endpoint accepts the standard page[size], page[after], and page[before] parameters.

The summaries are BookingSummary rather than the full Booking the endpoint used to return, matching the other list endpoints; use GET /v0/bookings/{id} to fetch one in full.


Standardize event-occurrences list pagination

2026-07-02

GET /v0/event-occurrences now accepts the standard page[size], page[after], and page[before] query parameters, matching the other list endpoints. The legacy pageSize, cursor[after], and cursor[before] parameters continue to work but are deprecated. The response schema is renamed to PagedOrganizerEventOccurrenceSummaries; the previous EventOccurrencesResponse name remains as a deprecated alias.


Paginate the races, tickets, and reserved-entry-groups lists

2026-07-01

The races list (GET /v0/event-occurrences/{id}/races), the event tickets list (GET /v0/events/{id}/tickets), and the reserved entry groups list (GET /v0/events/{id}/reserved-entries) now return a page cursor alongside data and accept page[size], page[after], and page[before], matching the other list endpoints. All continue to return every item by default, so existing integrations are unaffected. The response schemas are renamed to PagedRaceSummaries, PagedTickets, and PagedReservedEntryGroups; the previous Races, Tickets, and ReservedEntryGroups schema names remain as deprecated aliases.


Read a single event occurrence

2026-07-01

You can retrieve an individual event occurrence with GET /v0/event-occurrences/{id}. Access is scoped to the organizer associated with your Public API credentials.


June 2026

2026-06-30

GET /v0/races/{id} now includes eventOccurrenceId and eventId, so you can navigate from a race back to its event occurrence and event. These fields are returned on the single-race response; the race lists stay lean.


Read a single race

2026-06-30

You can retrieve an individual race with GET /v0/races/{id}. Access is scoped to the organizer that owns the race's event occurrence.


Find the booking forms that use a field

2026-06-30

You can look up which booking forms reference a given field with GET /v0/booking-form-fields/{id}/booking-forms. A field can be shared across many forms, so this returns a paginated list.


Read booking form fields

2026-06-30

You can read the individual questions on a booking form. Use GET /v0/booking-form-fields to list fields, GET /v0/booking-form-fields/{id} to fetch one, and GET /v0/booking-forms/{id}/fields to list the fields on a specific form. Each field exposes its type, label, options, and whether an answer is required.


Read booking forms

2026-06-30

You can retrieve a single booking form with GET /v0/booking-forms/{id}, and traverse between tickets and their booking forms: GET /v0/tickets/{id}/booking-forms lists the booking forms a ticket uses (usually zero or one), and GET /v0/booking-forms/{id}/tickets lists the tickets that share a booking form.


Read individual tickets and a race's tickets

2026-06-30

You can retrieve a single ticket with GET /v0/tickets/{id}, and list the tickets belonging to a race with GET /v0/races/{id}/tickets. Ticket titles now include a titleByLocale map, always seeded with the default (en-GB) title alongside any translations.


Races by event occurrence

2026-06-01

You can retrieve races for an event occurrence. Use GET /v0/event-occurrences/{id}/races if your integration needs race-level data.


May 2026

Application notes

2026-05-26

Application responses include stand-alone notes added from the dashboard. Use the notes field on application responses to read notes that are not approval or rejection reasons.

Participant booking source

2026-05-26

Participant responses include bookingSource. Use this field to distinguish bookings made through Let's Do This from participants imported from an external platform.

GivenGain partner lookup

2026-05-22

Partner lookups can use givenGain as an external ID type. Use externalId=givenGain when you need to query partners by their GivenGain identifier.

Team roles on participants

2026-05-20

When ?features=teams is enabled, participant team objects include roles with the participant's team role descriptions.

Clearer errors for invalid IDs

2026-05-08

Requests with a malformed resource ID in the path (for example, GET /v0/participants/not-a-valid-id) now return 404 Not Found instead of 500 Internal Server Error.


April 2026

Implied donation field on applications

2026-04-14

Applications for tickets where donation is implied by the ticket include a synthetic hasDonated field, so integrations receive the same shape as tickets that ask the donation question directly.


March 2026

Charity opt-in partner names

2026-03-11

Participant charityOptIn values return partner names instead of opaque partner IDs where the partner can be resolved.