gamexo developers
Booking integrationsPlatforms

Playo

Based on Playo External Venue Integration v2.0

gamexo implements Playo's External Venue Integration API Specifications v2.0, both the order-based and the direct booking flow. Playo calls gamexo, and gamexo is the source of truth for court availability.

Get a test environment

Email gamexo.confirmations@gmail.com to have a test environment set up. You will receive:

  • the base URL, ending in /api/v1/gateway
  • an API key for a test venue
  • the venue, sport and court IDs to configure

Live venues are onboarded the same way, each with its own key.

Authentication

Send the venue's key on every request:

X-API-Key: <venue API key>
Accept: application/json

The key alone identifies the venue: no other header is needed, and the base URL is the same for every venue. A missing, wrong or revoked key gets 401.

Conventions

  • Field names are camelCase: courtId, startTime, paidAtPlayo.
  • IDs for venues, sports and courts are gamexo UUIDs (36-character strings). The order and booking IDs we return look like XCB0042.
  • Times are the venue's local time, with date and time sent separately: "2026-12-12" and "18:00:00", no offset. An endTime at or before startTime ends the next day.
  • Money is INR, GST-inclusive. The price you send is what gets booked.
  • Customer details: if user sharing is off, 9999999999 and playo@playo.co are accepted.

The requestStatus envelope

Every response carries a string requestStatus: "1" for success, "0" for failure, with the reason in message.

{ "requestStatus": "1", "message": "Orders confirmed.", "bookingIds": [] }

A taken slot is a 200, not a 409

Business refusals, such as a slot that has just been taken or an unknown court, return 200 OK with requestStatus: "0". Retrying them unchanged will not help. Only genuine faults (a bad key, a malformed body, a server error) are HTTP errors, so check requestStatus on every 200.

Endpoints

Paths are relative to the base URL.

Fetch availability

GET /availability?venue_id=…&sport_id=…&date=2026-12-12

Returns every court for the sport, with every slot of the day at the venue's slot length (usually 60 minutes). It reflects all bookings at the venue, including walk-ins, other platforms and your unconfirmed holds.

{
  "requestStatus": "1",
  "message": "OK",
  "courts": [
    {
      "courtId": "5f2c8a1e-…",
      "courtName": "Court 1",
      "slots": [
        { "startTime": "18:00:00", "endTime": "19:00:00", "available": true, "ticketsAvailable": 1 },
        { "startTime": "19:00:00", "endTime": "20:00:00", "available": false, "ticketsAvailable": 0 }
      ]
    }
  ]
}

available is the field that decides. gamexo sells whole courts, so ticketsAvailable is always 1 or 0.

Create an order (hold)

POST /order/create
{
  "venueId": "…",
  "userName": "Ana Rao",
  "userMobile": "9000011111",
  "userEmail": "ana@example.com",
  "orders": [
    {
      "date": "2026-12-12",
      "courtId": "5f2c8a1e-…",
      "startTime": "18:00:00",
      "endTime": "19:00:00",
      "price": 1200.0,
      "paidAtPlayo": 1200.0,
      "playoOrderId": "100001"
    }
  ]
}

Holds the slots for 15 minutes while the customer pays. endTime is optional and defaults to the venue's slot length.

{
  "requestStatus": "1",
  "message": "Slots held.",
  "orderIds": [{ "externalOrderId": "XCB0042", "playoOrderId": "100001" }]
}

All or nothing: if any slot fails, none are held. Idempotent on playoOrderId.

Confirm an order

POST /order/confirm

Send the externalOrderId values returned by /order/create:

{ "orderIds": ["XCB0042"] }
{
  "requestStatus": "1",
  "message": "Orders confirmed.",
  "bookingIds": [{ "externalBookingId": "XCB0042", "playoOrderId": "100001" }]
}

All or nothing, and idempotent. A lapsed hold still confirms if the court is still free.

Create a booking directly

POST /booking/create

The single-step alternative to order and confirm. The body is the same as /order/create, with the slots under bookings. playoOrderId may be sent as an integer. clubDiscount is recorded but not applied to price.

Cancel an order

POST /order/cancel
{ "orderIds": ["XCB0042"] }

Releases the holds, and cancels any confirmed bookings for those orders too. Idempotent.

Cancel a booking

POST /booking/cancel
{
  "bookingIds": [
    { "playoOrderId": "100001", "externalBookingId": "XCB0042", "price": 1200.0, "refundAtPlayo": 1200.0 }
  ]
}

Frees the courts immediately. All or nothing. refundAtPlayo is recorded, but gamexo issues no refund.

Map booking IDs

POST /booking/map
{ "bookingIds": [{ "externalBookingId": "XCB0042", "playoBookingId": "900001" }] }

Optional. Stores your playoBookingId, so venue staff can quote it to Playo support.

GET /availability for the date.

POST /order/create. The slots are held for 15 minutes.

The customer pays on Playo.

POST /order/confirm with the externalOrderId.

POST /booking/map with your playoBookingId (optional).

If checkout is abandoned, send /order/cancel. Otherwise the hold lapses after 15 minutes.

Changes made by the venue

The v2.0 spec has no call from the venue to Playo, so:

  • Reschedule: the venue moves the booking in gamexo. It keeps its externalBookingId and price, and your next /availability call reflects the change. The venue informs the customer.
  • Cancel: the venue asks Playo to cancel, since Playo holds the payment. The court stays blocked until your /booking/cancel arrives.

An API or webhook for cancellations and reschedules started by the venue would let gamexo sync both automatically.

HTTP status codes

StatusMeaningRetry?
200, requestStatus: "1"Success—
200, requestStatus: "0"Business refusal; read messageNot unchanged
401Missing, wrong or revoked keyAfter fixing the key
422Malformed bodyAfter fixing the request
5xxServer errorYes, with backoff

Every endpoint is safe to retry with the same body. Keep playoOrderId stable across retries, because it is the idempotency key.

On this page