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/jsonThe 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. AnendTimeat or beforestartTimeends the next day. - Money is INR, GST-inclusive. The
priceyou send is what gets booked. - Customer details: if user sharing is off,
9999999999andplayo@playo.coare 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-12Returns 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/confirmSend 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/createThe 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.
Recommended flow
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
externalBookingIdand price, and your next/availabilitycall 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/cancelarrives.
An API or webhook for cancellations and reschedules started by the venue would let gamexo sync both automatically.
HTTP status codes
| Status | Meaning | Retry? |
|---|---|---|
200, requestStatus: "1" | Success | — |
200, requestStatus: "0" | Business refusal; read message | Not unchanged |
401 | Missing, wrong or revoked key | After fixing the key |
422 | Malformed body | After fixing the request |
5xx | Server error | Yes, with backoff |
Every endpoint is safe to retry with the same body. Keep playoOrderId stable across
retries, because it is the idempotency key.