openapi: "3.1.0" info: title: REST API v1 version: "1" description: | REST API for third-party integrations, allowing access to all important data across your events. Get started by creating an OAuth client in the My Apps page, or connect instantly with the Zapier app. servers: - url: https://www.raceentry.com/restapi/v1 description: Race Entry - url: https://www.rodeoticket.com/restapi/v1 description: Rodeo Ticket - url: https://www.strideevents.com/restapi/v1 description: Stride Events - url: https://demo.raceentry.com/restapi/v1 description: Race Entry Test - url: https://demo.rodeoticket.com/restapi/v1 description: Rodeo Ticket Test - url: https://demo.strideevents.com/restapi/v1 description: Stride Events Test security: - oauth2: [] paths: # ─── Events ────────────────────────────────────────────────────────────────── /events: get: operationId: listEvents summary: List authorized events description: Returns every event this token's `event:{id}` scopes grant access to, intersected with the user's live permissions. tags: [Events] responses: "200": description: List of events content: application/json: schema: type: object required: [data, count] properties: data: type: array items: $ref: "#/components/schemas/Event" count: type: integer example: 3 "401": $ref: "#/components/responses/Unauthorized" /events/{event_id}: get: operationId: getEvent summary: Get a single event tags: [Events] parameters: - $ref: "#/components/parameters/event_id_path" responses: "200": description: Event detail content: application/json: schema: $ref: "#/components/schemas/Event" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" # ─── Orders ────────────────────────────────────────────────────────────────── /orders: get: operationId: listOrders summary: List completed orders description: | Completed orders for an event year, newest first, with a full per-order price breakdown. Mirrors the web Orders report. Order-type reallocation is applied: for ticket-style order types, paid answers move into `ticket_total`; for photo order types `photos_total` folds into `registration_total`. Required permissions: Sales Rep, Director, Reports, Financials, or Sponsors. tags: [Orders] parameters: - $ref: "#/components/parameters/event_id_query" - $ref: "#/components/parameters/year" - $ref: "#/components/parameters/since" - $ref: "#/components/parameters/page" - $ref: "#/components/parameters/per_page" responses: "200": description: Paginated order list content: application/json: schema: $ref: "#/components/schemas/PaginatedOrders" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" /orders/{id}: get: operationId: getOrder summary: Get a single order with line items description: | Returns one completed order with nested `line_items` containing registrations, tickets, merchandise, and donations. Required permissions: Sales Rep, Director, Reports, Financials, or Sponsors. tags: [Orders] parameters: - name: id in: path required: true schema: type: integer description: Order id responses: "200": description: Order detail with line items content: application/json: schema: $ref: "#/components/schemas/OrderDetail" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" # ─── Participants ───────────────────────────────────────────────────────────── /participants: get: operationId: listParticipants summary: List registered participants description: | Confirmed registrations for an event year, newest first. Intended for CRM sync as the "New Registrant" / "New Contact" trigger. Required permissions: Sales Rep, Director, Reports, Participants, or Timing. tags: [Customers] parameters: - $ref: "#/components/parameters/event_id_query" - $ref: "#/components/parameters/year" - $ref: "#/components/parameters/since" - $ref: "#/components/parameters/sort" - $ref: "#/components/parameters/checked_in" - $ref: "#/components/parameters/page" - $ref: "#/components/parameters/per_page" responses: "200": description: Paginated participant list content: application/json: schema: type: object allOf: - $ref: "#/components/schemas/PaginationEnvelope" - type: object properties: data: type: array items: $ref: "#/components/schemas/Participant" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" # ─── Attendees ─────────────────────────────────────────────────────────────── /attendees: get: operationId: listAttendees summary: List ticket holders description: | Ticket groups (ticket holders) for completed orders in an event year, newest first by order date. Intended for CRM sync as the "New Attendee" trigger. Required permissions: Sales Rep, Director, Reports, Attendees, or Timing. tags: [Customers] parameters: - $ref: "#/components/parameters/event_id_query" - $ref: "#/components/parameters/year" - $ref: "#/components/parameters/since" - $ref: "#/components/parameters/sort" - $ref: "#/components/parameters/checked_in" - $ref: "#/components/parameters/page" - $ref: "#/components/parameters/per_page" responses: "200": description: Paginated attendee list content: application/json: schema: type: object allOf: - $ref: "#/components/schemas/PaginationEnvelope" - type: object properties: data: type: array items: $ref: "#/components/schemas/Attendee" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" # ─── Sponsors ──────────────────────────────────────────────────────────────────── /sponsors: get: operationId: listSponsors summary: List active sponsors description: | Active sponsors for an event, ordered by category priority then sponsor priority. Sponsors are event-level, not year-level — the `year` parameter is accepted only to resolve which event is requested, and does not filter sponsors. Required permissions: Sales Rep, Director, Reports, or Sponsors. tags: [Sponsors] parameters: - $ref: "#/components/parameters/event_id_query" - $ref: "#/components/parameters/year" - $ref: "#/components/parameters/page" - $ref: "#/components/parameters/per_page" responses: "200": description: Paginated sponsor list content: application/json: schema: type: object allOf: - $ref: "#/components/schemas/PaginationEnvelope" - type: object properties: data: type: array items: $ref: "#/components/schemas/Sponsor" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" # ─── Donations ────────────────────────────────────────────────────────────────── /donations: get: operationId: listDonations summary: List charity donations description: | Charity donations for completed orders in an event year, newest first by order date. Returns the order's billing contact alongside each donation so a donor can be synced into a CRM even when `donor_name` is anonymized for public display. The `hide_name` and `hide_amount` flags are passed through so consumers can honor them if desired. Required permissions: Sales Rep, Director, Charity, or Reports. tags: [Customers] parameters: - $ref: "#/components/parameters/event_id_query" - $ref: "#/components/parameters/year" - $ref: "#/components/parameters/since" - $ref: "#/components/parameters/page" - $ref: "#/components/parameters/per_page" responses: "200": description: Paginated donation list content: application/json: schema: type: object allOf: - $ref: "#/components/schemas/PaginationEnvelope" - type: object properties: data: type: array items: $ref: "#/components/schemas/Donation" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" # ─── Components ──────────────────────────────────────────────────────────────── components: securitySchemes: oauth2: type: oauth2 description: | OAuth 2 authorization-code flow. Request the `events` scope to be offered the event picker during consent. The issued token carries `event:{id}` scopes for each event the user selected. flows: authorizationCode: authorizationUrl: /oauth2/authorize tokenUrl: /oauth2/token scopes: events: Access to event data. Replaced with specific event:{id} scopes at consent time. parameters: event_id_path: name: event_id in: path required: true schema: type: integer description: Event id event_id_query: name: event_id in: query required: true schema: type: integer description: Event id — must match an event in the token's granted scopes. year: name: year in: query required: false schema: type: string pattern: "^\\d{4}$" example: "2026" description: Four-digit year. Defaults to the event's most recent event year. since: name: since in: query required: false schema: type: string format: date-time example: "2026-01-01T00:00:00Z" description: | ISO 8601 timestamp. Only records created/completed on or after this time are returned. Ideal for Zapier polling and incremental CRM sync. page: name: page in: query required: false schema: type: integer minimum: 1 default: 1 per_page: name: per_page in: query required: false schema: type: integer minimum: 1 maximum: 100 default: 50 sort: name: sort in: query required: false schema: type: string enum: [checked_in] description: | Sort order. `checked_in` sorts by check-in time descending (most recently checked-in first). Omit for the default sort (newest record first). Combine with `checked_in=true` to poll for recent check-ins. checked_in: name: checked_in in: query required: false schema: type: boolean description: | When `true`, only records that have been checked in are returned. Omit or `false` to return all records regardless of check-in status. responses: BadRequest: description: Missing or invalid query parameter content: application/json: schema: $ref: "#/components/schemas/Error" example: error: missing_event_id message: An event_id query parameter is required. Unauthorized: description: Missing, invalid, or expired bearer token content: application/json: schema: $ref: "#/components/schemas/Error" example: error: invalid_token message: The JWT string must have two dots Forbidden: description: Token not scoped to the event, or insufficient web permission content: application/json: schema: $ref: "#/components/schemas/Error" example: error: permission_denied message: You do not have permission to access this event's data. NotFound: description: Event, event year, or record not found content: application/json: schema: $ref: "#/components/schemas/Error" example: error: event_not_found message: Event not found. schemas: Error: type: object required: [error, message] properties: error: type: string description: Machine-readable error code example: permission_denied message: type: string description: Human-readable description example: You do not have permission to access this event's data. PaginationEnvelope: type: object required: [data, page, per_page, count, next_page] properties: page: type: integer example: 1 per_page: type: integer example: 50 count: type: integer description: Number of records in this page example: 50 next_page: type: integer nullable: true description: Next page number, or null when on the last page example: 2 EventYear: type: object required: [event_year_id, year, status_id] properties: event_year_id: type: integer example: 456 year: type: string example: "2026" status_id: type: integer example: 1 Event: type: object required: [id, name, url, site, currency_symbol, event_years] properties: id: type: integer example: 123 name: type: string example: Spring 10K url: type: string description: URL slug used in the event's web address example: spring-10k site: type: string description: Brand this event belongs to enum: [Race Entry, Rodeo Ticket, Stride Events] example: Race Entry currency_symbol: type: string example: "$" event_years: type: array items: $ref: "#/components/schemas/EventYear" # Monetary fields are returned as strings from MySQL decimal columns. # They are always formatted to 2 decimal places. PriceBreakdown: type: object description: Per-order price breakdown. All amounts are decimal strings (2 d.p.). properties: registration_total: type: string description: | Sum of registration prices + paid-answer charges (for non-ticket order types) + photo total (for photo order types). example: "45.00" ticket_total: type: string description: Sum of ticket prices + paid-answer charges (for ticket order types). example: "0.00" merchandise_total: type: string example: "25.00" photos_total: type: string example: "0.00" photo_fee_total: type: string example: "0.00" sponsor_total: type: string example: "0.00" charity_total: type: string description: Aggregated charity donation amounts for this order. example: "10.00" tip_total: type: string example: "0.00" tax_total: type: string example: "3.50" marketplace_tax_total: type: string description: Combined marketplace tax and marketplace shipping tax. example: "0.00" discount_total: type: string description: Promo-code, social, and referral discounts applied to the order. example: "5.00" change_total: type: string description: Registration-edit and transfer fees. example: "0.00" fee_total: type: string description: Processing fees including per-donation fees. example: "2.25" bonus_fee_total: type: string description: Event bonus fees (a subset of fee_total). example: "0.00" gift_card_payment: type: string description: Amount paid by gift card. example: "0.00" order_total: type: string description: Authoritative grand total stored on the order. example: "80.75" BillingContact: type: object properties: billing_name: type: string nullable: true example: Jane Smith billing_email: type: string format: email nullable: true example: jane@example.com billing_address: type: string nullable: true example: 123 Main St billing_city: type: string nullable: true example: Denver billing_state: type: string nullable: true description: US state abbreviation example: CO billing_province: type: string nullable: true description: Province/region for non-US addresses example: null billing_zip: type: string nullable: true example: "80203" billing_country: type: string nullable: true description: ISO 3166-1 alpha-3 country code example: USA Order: allOf: - $ref: "#/components/schemas/BillingContact" - $ref: "#/components/schemas/PriceBreakdown" - type: object required: [id, finish_date, event_year_id, type_id, payment_type_id, order_total] properties: id: type: integer example: 78901 finish_date: type: string format: date-time description: When the order was completed (payment captured) example: "2026-03-15T14:22:00-06:00" event_year_id: type: integer example: 456 order_type: type: string nullable: true description: Order type name example: Online payment_type: type: string description: "One of: Credit/Debit, Cash, Check, Upload, Invoice, Split" example: Credit/Debit ccnumber: type: string description: Masked card number (xxxx-xxxx-xxxx-1234) nullable: true example: xxxx-xxxx-xxxx-4242 transaction_id: type: string nullable: true description: Processor transaction id example: ch_3OxYz2K RegistrationLineItem: type: object required: [id] properties: id: type: integer example: 11234 order_id: type: integer example: 78901 first_name: type: string example: Jane last_name: type: string example: Smith email: type: string format: email example: jane@example.com category: type: string nullable: true example: 5K Open Female bib_number: type: integer nullable: true example: 42 price: type: string example: "45.00" promo_amount: type: string example: "5.00" tax_collected: type: string example: "0.00" processing_fee: type: string nullable: true example: "2.25" TicketLineItem: type: object required: [id] properties: id: type: integer example: 55678 event_time_name: type: string description: What named event time this ticket is for nullable: true example: "" event_time_start: type: string format: date-time description: What time the ticket event starts example: "2026-07-15T18:30:00-06:00" price_label: type: string nullable: true example: General Admission price: type: string example: "35.00" tax: type: string example: "0.00" fee: type: string example: "1.75" bonus_fee: type: string example: "0.00" marketplace_tax: type: string example: "0.00" promo_amount: type: string example: "0.00" social_discount_amount: type: string example: "0.00" MerchandiseLineItem: type: object required: [id] properties: id: type: integer example: 33456 name: type: string example: Race T-Shirt size: type: string nullable: true example: M quantity: type: integer example: 1 price: type: string example: "25.00" subtotal: type: string example: "25.00" shipping: type: string example: "0.00" tax_collected: type: string example: "0.00" processing_fee: type: string example: "0.00" total: type: string example: "25.00" DonationLineItem: type: object required: [id, amount, fee, total] properties: id: type: integer example: 9012 donor_name: type: string nullable: true example: Jane Smith charity: type: string nullable: true example: American Heart Association amount: type: string example: "10.00" fee: type: string example: "0.30" total: type: string example: "10.30" comment: type: string nullable: true example: "Keep up the great work!" hide_name: type: boolean description: donor chose to hide their name from public display example: false hide_amount: type: boolean description: donor chose to hide their amount from public display example: false OrderDetail: allOf: - $ref: "#/components/schemas/Order" - type: object required: [line_items] properties: line_items: type: object required: [registrations, tickets, merchandise, donations] properties: registrations: type: array items: $ref: "#/components/schemas/RegistrationLineItem" tickets: type: array items: $ref: "#/components/schemas/TicketLineItem" merchandise: type: array items: $ref: "#/components/schemas/MerchandiseLineItem" donations: type: array items: $ref: "#/components/schemas/DonationLineItem" PaginatedOrders: allOf: - $ref: "#/components/schemas/PaginationEnvelope" - type: object properties: data: type: array items: $ref: "#/components/schemas/Order" Participant: type: object required: [id, email, first_name, last_name, registration_date] properties: id: type: integer example: 11234 first_name: type: string example: Jane last_name: type: string example: Smith email: type: string format: email example: jane@example.com phone: type: string example: "303-555-0100" address: type: string example: 123 Main St city: type: string example: Denver state: type: string nullable: true description: Full US state name example: Colorado province: type: string nullable: true example: null zip: type: string example: "80203" country_code: type: string nullable: true description: ISO 3166-1 alpha-3 country code example: USA gender: type: string enum: [M, F, ""] example: F birthdate: type: string format: date example: "1990-04-15" bib_number: type: integer nullable: true example: 42 category: type: string nullable: true example: 5K Open Female registration_date: type: string format: date-time example: "2026-01-10T09:30:00-07:00" checked_in_at: type: string format: date-time nullable: true description: When the participant was checked in, or null if not yet checked in. example: "2026-06-01T08:30:00-06:00" AttendeeTicket: type: object required: [id, event_time_name, event_time_start] properties: id: type: integer example: 55678 event_time_name: type: string description: Name of the event time (show/performance) this ticket is for example: Saturday Evening Show event_time_start: type: string format: date-time description: Start datetime of the event time example: "2026-07-15T18:30:00-06:00" section_name: type: string nullable: true description: Venue section name, or null for general admission example: Floor row_name: type: string nullable: true description: Row within the section, or null for general admission example: A seat_name: type: string nullable: true description: Specific seat label, or null for general admission example: "14" price_label: type: string nullable: true description: Price tier label (e.g. General Admission, VIP) example: General Admission checked_in_at: type: string format: date-time nullable: true description: Time of first ticket scan (check-in), or null if not yet scanned. example: "2026-07-15T18:45:00-06:00" Attendee: type: object required: [id, order_id, order_date, tickets] properties: id: type: integer description: Ticket group id example: 7890 name: type: string nullable: true description: Ticket holder name (as entered at ticket selection) example: Jane Smith email: type: string format: email nullable: true example: jane@example.com phone: type: string nullable: true description: Ticket holder's SMS / phone number example: "303-555-0100" status: type: string description: "Ticket group status: original, print_only, forward_pending, forward_claimed" example: original order_id: type: integer example: 78901 billing_name: type: string nullable: true example: Jane Smith billing_email: type: string format: email nullable: true example: jane@example.com order_date: type: string format: date-time example: "2026-02-20T16:45:00-07:00" tickets: type: array description: Individual tickets in this group, each with seat and event time details items: $ref: "#/components/schemas/AttendeeTicket" Sponsor: type: object required: [id, name, category_id, category] properties: id: type: integer example: 123 name: type: string example: ACME Corp category: type: string nullable: true description: Sponsor tier/category name example: Gold category_id: type: integer example: 45 email: type: string format: email nullable: true example: sponsor@acme.com phone: type: string nullable: true example: "303-555-0200" website: type: string nullable: true example: "https://acme.com" Donation: type: object required: [id, amount, fee, total, order_id, order_date] properties: id: type: integer example: 9012 donor_name: type: string nullable: true description: Donor-supplied display name (may be null if not provided) example: Jane Smith charity: type: string nullable: true example: American Heart Association amount: type: string example: "10.00" fee: type: string description: Processing fee applied to the donation example: "0.30" total: type: string description: amount + fee example: "10.30" comment: type: string nullable: true example: "Great cause!" hide_name: type: bool description: donor chose to hide their name from public display example: false hide_amount: type: bool description: donor chose to hide their amount from public display example: false order_id: type: integer example: 78901 billing_name: type: string nullable: true description: Billing name from the order (always present, unaffected by hide_name) example: Jane Smith billing_email: type: string format: email nullable: true example: jane@example.com order_date: type: string format: date-time example: "2026-03-15T14:22:00-06:00" tags: - name: Events description: Event and event-year discovery - name: Orders description: Financial — completed orders with full price breakdown and line items - name: Customers description: CRM contact feeds — participants, attendees, merchandise customers, and donors - name: Sponsors description: Active event sponsors