> ## Documentation Index
> Fetch the complete documentation index at: https://docs.laces.run/llms.txt
> Use this file to discover all available pages before exploring further.

# Get an event

> One event with its ticket types and the fees a participant pays for each.



## OpenAPI

````yaml api-reference/v1/openapi.json GET /events/{eventId}
openapi: 3.1.0
info:
  title: Laces Partner API
  version: 1.0.0
  summary: Show your organization's races and sell entries from your own website.
  description: >-
    The Laces Partner API, served at `https://connect.laces.run/v1`. Every path
    is relative to that server URL, which already ends in `/v1`.


    Authenticate with a **bearer token** from `POST /oauth/token` (server side)
    or, on the routes that allow it, a **publishable key** in
    `X-Laces-Publishable-Key` (browser side, from an allowed domain). Keys are
    uppercase and case-sensitive: send them exactly as issued.


    There is one environment, production. Payments made through the API are
    real.


    Money is integer minor units. Every route except the token endpoint answers
    in the Laces envelope `{ code, message, data | error, error_code }`; branch
    on `error_code`, never on `message` or `error`.


    Every response carries an `X-Request-Id` header (`req_…`). Quote it to
    support@laces.run when something goes wrong. Browsers can read
    `X-Request-Id` and `Retry-After` (both are CORS-exposed). Rate-limited
    routes also send the standard `RateLimit-Limit`, `RateLimit-Remaining` and
    `RateLimit-Reset` headers.


    Partner credentials are accepted on this host only. Sent to `api.laces.run`
    (the Laces app's own API), any partner credential is refused with `401
    partner_credential_not_accepted`.
  contact:
    name: Laces support
    email: support@laces.run
    url: https://docs.laces.run
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary
servers:
  - url: https://connect.laces.run/v1
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Authentication
    description: OAuth 2.0 client credentials.
  - name: Events
    description: Your organization's races that are on sale.
  - name: Checkout sessions
    description: 'Laces-hosted checkout: redirect or embedded.'
  - name: Orders
    description: Create, pay and read orders from your own UI.
  - name: Webhook events
    description: Requests Laces sends to your webhook endpoint.
  - name: Health
    description: Liveness probe.
paths:
  /events/{eventId}:
    get:
      tags:
        - Events
      summary: Get an event
      description: >-
        The detail view for a ticket picker: ticket types with **server-computed
        fees** for each payment method, order-level questions, and the waiver
        flag.


        Show the fees as returned; the order response's `total` is
        authoritative. Readable exactly when the event appears in the list.
      operationId: getEvent
      parameters:
        - name: eventId
          in: path
          required: true
          schema:
            type: string
          example: q3Zk9mVx
      responses:
        '200':
          description: The event.
          content:
            application/json:
              schema:
                type: object
                required:
                  - code
                  - message
                  - data
                properties:
                  code:
                    type: integer
                    description: HTTP status, repeated in the body.
                    examples:
                      - 200
                  message:
                    type: string
                    examples:
                      - Event retrieved successfully
                  data:
                    $ref: '#/components/schemas/EventDetail'
              example:
                code: 200
                message: Event retrieved successfully
                data:
                  event_id: q3Zk9mVx
                  name: Kampala City 10K
                  handle: kampala-city-10k
                  short_code: K10
                  description: A flat, fast 10K through the city centre.
                  cover: https://laces.run/images/example-cover.jpg
                  venue: Kololo Airstrip
                  event_type: run
                  status: open
                  visibility: public
                  starts_at: '2026-11-15T04:00:00.000Z'
                  ends_at: '2026-11-15T09:00:00.000Z'
                  timezone: Africa/Kampala
                  currency: UGX
                  reg_opens_at: '2026-09-01T00:00:00.000Z'
                  reg_closes_at: '2026-11-13T20:00:00.000Z'
                  payments_enabled: true
                  organizer_name: Kampala Road Runners
                  support_email: hello@example.com
                  payment_methods:
                    - momo
                    - airtel
                  ticket_types:
                    - ticket_type_id: tt_10k
                      name: 10K
                      description: Standard entry
                      currency: UGX
                      price_minor: 50000
                      laces_fee_minor: 2400
                      total_minor: 52400
                      card: null
                      airtel:
                        laces_fee_minor: 2400
                        total_minor: 52400
                      is_free: false
                      capacity: 500
                      distance_km: 10
                      remaining: 212
                      sales_start: '2026-09-01T00:00:00.000Z'
                      sales_end: '2026-11-13T20:00:00.000Z'
                      min_age: 16
                  additional_questions:
                    - key: kit_size
                      label: T-shirt size
                      type: select
                      required: true
                      options:
                        - S
                        - M
                        - L
                        - XL
                      help: ''
                  waiver_required: true
                  waiver_version: '2026-09-01'
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
      security:
        - bearerAuth: []
        - oauth2:
            - events:read
        - publishableKey: []
components:
  schemas:
    EventDetail:
      allOf:
        - $ref: '#/components/schemas/EventSummary'
        - type: object
          properties:
            organizer_name:
              type: string
              examples:
                - Kampala Road Runners
            support_email:
              type: string
              description: Omitted when the organization has none.
              format: email
            payment_methods:
              type: array
              items:
                type: string
                enum:
                  - momo
                  - card
                  - airtel
              description: >-
                The payment methods you can sell for this event right now. Today
                `["momo", "airtel"]`: MTN Mobile Money and Airtel Money. Card is
                coming soon; it will appear here when it is available. Offer
                only these methods; any other is refused at order creation with
                `409 payment_method_unavailable`.
              examples:
                - - momo
                  - airtel
            ticket_types:
              type: array
              items:
                $ref: '#/components/schemas/TicketType'
            additional_questions:
              type: array
              items:
                $ref: '#/components/schemas/AdditionalQuestion'
            waiver_required:
              type: boolean
              description: >-
                When true, collect the participant's agreement and send
                `waiver_accepted: true` and `waiver_version` on order creation.
            waiver_version:
              type:
                - string
                - 'null'
              examples:
                - '2026-09-01'
          required:
            - organizer_name
            - payment_methods
            - ticket_types
            - additional_questions
            - waiver_required
    EventSummary:
      type: object
      properties:
        event_id:
          type: string
          examples:
            - q3Zk9mVx
        name:
          type: string
          examples:
            - Kampala City 10K
        handle:
          type: string
          examples:
            - kampala-city-10k
        short_code:
          type: string
          examples:
            - K10
        description:
          type: string
        cover:
          type: string
          description: Cover image URL.
          format: uri
        venue:
          type: string
          description: Omitted when not set.
        event_type:
          type: string
          examples:
            - run
        status:
          type: string
          enum:
            - scheduled
            - open
        visibility:
          type: string
          description: Unlisted events are included on purpose.
          enum:
            - public
            - unlisted
        starts_at:
          type: string
          format: date-time
        ends_at:
          type: string
          format: date-time
        timezone:
          type: string
          examples:
            - Africa/Kampala
        currency:
          type: string
          examples:
            - UGX
        reg_opens_at:
          type: string
          description: Omitted when not set.
          format: date-time
        reg_closes_at:
          type: string
          description: Omitted when not set.
          format: date-time
        payments_enabled:
          type: boolean
          description: >-
            Organization-level. `false` means your organization is not yet
            cleared to take payments: the event stays readable, but orders and
            checkout sessions are refused with `403 payments_disabled`.
      required:
        - event_id
        - name
        - handle
        - short_code
        - description
        - cover
        - event_type
        - status
        - visibility
        - starts_at
        - ends_at
        - timezone
        - currency
        - payments_enabled
    TicketType:
      type: object
      properties:
        ticket_type_id:
          type: string
          examples:
            - tt_10k
        name:
          type: string
          examples:
            - 10K
        description:
          type: string
        currency:
          type: string
          examples:
            - UGX
        price_minor:
          type: integer
          description: The organizer's price, minor units.
        laces_fee_minor:
          type:
            - integer
            - 'null'
          description: >-
            Laces fee by MTN mobile money (`momo`, the default method). `null` =
            could not be priced; treat the ticket as unavailable.
        total_minor:
          type:
            - integer
            - 'null'
          description: >-
            What the participant pays by mobile money: `price_minor +
            laces_fee_minor`.
        card:
          oneOf:
            - type: object
              properties:
                laces_fee_minor:
                  type: integer
                total_minor:
                  type: integer
              required:
                - laces_fee_minor
                - total_minor
            - type: 'null'
          description: >-
            Card fee and total for one ticket bought on its own. **`null`
            today**: card is not yet available to partners (it is coming soon).
            Once it is, `card` is listed in the event's `payment_methods` and
            this holds the card quote.
        airtel:
          oneOf:
            - type: object
              properties:
                laces_fee_minor:
                  type: integer
                total_minor:
                  type: integer
              required:
                - laces_fee_minor
                - total_minor
            - type: 'null'
          description: >-
            Fee and total for this payment method, for one ticket bought on its
            own. `null` if it could not be priced.
        is_free:
          type: boolean
          description: >-
            `price_minor` is 0. A free order needs no payment and no phone
            number.
        capacity:
          type: integer
        distance_km:
          type: number
          description: Omitted when not set.
        remaining:
          type: integer
          description: Present only when the organizer shows remaining places.
        sales_start:
          type: string
          format: date-time
        sales_end:
          type: string
          format: date-time
        min_age:
          type: integer
        max_age:
          type: integer
      required:
        - ticket_type_id
        - name
        - description
        - currency
        - price_minor
        - laces_fee_minor
        - total_minor
        - card
        - airtel
        - is_free
        - capacity
      description: A ticket type with server-computed fees. Never calculate fees yourself.
    AdditionalQuestion:
      type: object
      properties:
        key:
          type: string
          examples:
            - kit_size
        label:
          type: string
          examples:
            - T-shirt size
        type:
          type: string
          enum:
            - text
            - select
        required:
          type: boolean
        options:
          type: array
          items:
            type: string
        help:
          type: string
      required:
        - key
        - label
        - type
        - required
        - options
        - help
      description: An order-level question. Send answers in `custom_fields` keyed by `key`.
    Error:
      type: object
      properties:
        code:
          type: integer
          description: HTTP status, repeated in the body.
        message:
          type: string
          description: Short outcome. Do not branch on it; it may be reworded.
        error:
          type: string
          description: Human-readable detail. May be reworded; never parse it.
        error_code:
          type: string
          description: >-
            Stable machine-readable code. **Branch on this.** Present on every
            Partner API error, including `429 rate_limited`, `404 not_found` for
            unknown routes and `500 server_error`. See the [error
            reference](/concepts/errors).
        details:
          type: object
          properties:
            retry_after_seconds:
              type: integer
          description: Only on `409 prompt_in_progress`.
      required:
        - code
        - message
        - error
        - error_code
      description: The Laces error envelope.
  headers:
    X-Request-Id:
      description: >-
        Unique id of this request (`req_…`). On every response, readable from
        browsers. Quote it to support.
      schema:
        type: string
      example: req_V1StGXR8Z5jdHi6BmyTq
    Retry-After:
      description: Whole seconds to wait before retrying. Readable from browsers.
      schema:
        type: integer
    RateLimit-Limit:
      description: Requests allowed in the current window.
      schema:
        type: integer
    RateLimit-Remaining:
      description: Requests left in the current window.
      schema:
        type: integer
    RateLimit-Reset:
      description: Seconds until the window resets.
      schema:
        type: integer
  responses:
    Unauthorized:
      description: >-
        No credential, an invalid or expired token, or an invalid publishable
        key. Comes with `WWW-Authenticate: Bearer realm="laces-partner"`.
        Revoking or regenerating keys can take up to a minute to reach every
        server.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            unauthorized:
              summary: unauthorized
              value:
                code: 401
                message: Unauthorized
                error: Missing credentials
                error_code: unauthorized
            invalid_token:
              summary: invalid_token
              value:
                code: 401
                message: Unauthorized
                error: Invalid or expired access token
                error_code: invalid_token
            invalid_key:
              summary: invalid_key
              value:
                code: 401
                message: Unauthorized
                error: Invalid publishable key
                error_code: invalid_key
      headers:
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
    Forbidden:
      description: >-
        The credential is not allowed here: a missing scope (scope changes take
        up to about a minute to apply), a publishable key on a bearer-only
        route, or a publishable key from an origin that is not an allowed
        domain.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            insufficient_scope:
              summary: insufficient_scope
              value:
                code: 403
                message: Forbidden
                error: 'Missing scope: orders:read'
                error_code: insufficient_scope
            credential_not_permitted:
              summary: credential_not_permitted
              value:
                code: 403
                message: Forbidden
                error: This endpoint does not accept a publishable key
                error_code: credential_not_permitted
            origin_not_allowed:
              summary: origin_not_allowed
              value:
                code: 403
                message: Forbidden
                error: This origin is not allowed for this key
                error_code: origin_not_allowed
      headers:
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
    NotFound:
      description: >-
        Not found, or it belongs to another organization (the same answer, so
        existence never leaks).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            not_found:
              summary: not_found
              value:
                code: 404
                message: Not found
                error: Event not found
                error_code: not_found
      headers:
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
    RateLimited:
      description: >-
        Rate limited. Wait `Retry-After` seconds. Limits are per API client
        (reads about 300 a minute, writes about 60) and, for publishable keys,
        also per IP (about 120 a minute).
      headers:
        Retry-After:
          $ref: '#/components/headers/Retry-After'
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimit-Limit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimit-Remaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimit-Reset'
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: 429
            message: Too many requests, please slow down
            error: rate_limited
            error_code: rate_limited
    ServerError:
      description: >-
        Unexpected failure. Retry with backoff, reusing the same
        `Idempotency-Key`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            server_error:
              summary: server_error
              value:
                code: 500
                message: An unexpected error occurred
                error: Something went wrong
                error_code: server_error
      headers:
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: LCSTOK-…-X (opaque)
      description: >-
        A token from `POST /oauth/token`, valid 1 hour. Paste it as `LCSTOK-…-X`
        (without the word Bearer).
    oauth2:
      type: oauth2
      description: >-
        OAuth 2.0 client credentials. The token it yields is the `bearerAuth`
        token.
      flows:
        clientCredentials:
          tokenUrl: https://connect.laces.run/v1/oauth/token
          scopes:
            events:read: List and read events
            orders:write: Create and pay orders
            orders:read: >-
              List and read orders, including buyer personal data (off by
              default)
            checkout_sessions:write: Create and read checkout sessions
    publishableKey:
      type: apiKey
      in: header
      name: X-Laces-Publishable-Key
      description: >-
        Browser-safe key `LCSPUBK-…-X`. Only works from a browser whose `Origin`
        is one of your allowed domains, and is capped to `events:read` and
        creating checkout sessions. **"Try it" with a publishable key fails in
        this playground** (`403 origin_not_allowed`): the request carries no
        allowed `Origin`. Use a bearer token here.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.