Skip to content

Public Events API

Five requests from the key to a ticket barcode. The samples below are set to the test environment — start there. A full description of every method is in the interactive reference, which lives on the same host as the API.

Method prefix
/api/v2
Authorization
Bearer JWT, 24 h
Pagination
page · itemsPerPage ≤ 100

The set of methods, the formats and the behavior are identical. The address, the data and the key differ.

Test · start here

Staging

https://st-pa.tbx.pp.ua

A sandbox for the integration: test events, free orders, refunds without real money. This is where you run the smoke test and debug your code.

Staging reference →

Live · after testing

Production

https://api.ticketcrm.net

Real events, real sales. You move here once the integration has passed on staging.

Production reference →

Each environment has its own key. For the live API we will issue a separate, new API key — the staging one does not work there. Moving to production is exactly two changes: a different address in the base URL and a new key. The code, the response structures and the logic stay the same.

Every sample below updates. Nothing is stored or sent anywhere — the values live only in your tab.

You get the token in step 1, the event and price category ids in steps 2 and 3, the order id in step 4. Come back here and fill them in as you go. The “Base URL” select at the top of the page changes the address in every sample; the key differs per environment too.

The order is mandatory: every step produces the identifier the next one needs.

Get a token

The key is exchanged for a JWT. The token lives for 24 hours — cache it instead of requesting one before every call. A staging key only works on staging; for production you will be issued a separate one.

curl -X POST "https://st-pa.tbx.pp.ua/api/v2/token" \
  -H "Content-Type: application/json" \
  -d '{"apiKey": "YOUR_KEY"}'

In the response: {"token": "eyJ0eXAiOiJKV1Qi…"}. From there it travels in the Authorization: Bearer … header of every request.

Pull the event catalog

The main endpoint of the integration. Your key only sees the events opened to you, and only those that are still on sale.

curl "https://st-pa.tbx.pp.ua/api/v2/event-points?itemsPerPage=20&order[dateStarted]=asc" \
  -H "Authorization: Bearer TOKEN" \
  -H "X-API-Language: en"

# filters you will need in production:
#   dateStarted[after]=2026-10-01&dateStarted[before]=2026-10-31
#   city.id[]=1&categories.id[]=3&genres.id[]=7&venueHall.id=42

Take from here: the event id for the next step. Export the reference lists /cities, /countries, /venues, /venue-halls, /categories, /genres once, separately, and cache them.

Look at sectors and places

The heaviest block by volume — request it when a specific event is opened, not across the whole catalog.

curl "https://st-pa.tbx.pp.ua/api/v2/event-points/{eventPointId}/sectors" \
  -H "Authorization: Bearer TOKEN"

curl "https://st-pa.tbx.pp.ua/api/v2/event-points/{eventPointId}/places?itemsPerPage=100" \
  -H "Authorization: Bearer TOKEN"

Take from here: the id of a free place and an id from its priceCategories — that is the price category you name in the order.

Create an order

The order goes straight into reserved_to_time. The default hold is 15 minutes.

curl -X POST "https://st-pa.tbx.pp.ua/api/v2/orders" \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "eventPoint": "{eventPointId}",
    "places": ["{placeId}"],
    "priceCategory": "{priceCategoryId}",
    "reservedMinutes": 15
  }'

In the response: the order id, status, reservedToDate and totalPrice. Extend the hold with PUT /orders/{id}/reserve, add to the cart with PUT /orders/{id}/add-place.

Pay and take the barcodes

You take the payment on your side and tell us the result with one request: move the order to paid. No separate call for the barcodes afterwards — they are already in the response.

curl -X PUT "https://st-pa.tbx.pp.ua/api/v2/orders/{orderId}/status" \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"toStatus": "paid"}'

The response is the whole order: status, totalPrice, totalDiscount and tickets, where every ticket carries ticketId, barcode, placeId, placeName, sectorName, rowName, price and discountPrice. That is enough to build your own ticket layout.

Both exist in the API, but the partner flow above does not need them — do not build them into your integration without a reason.

GET /orders/{id}/payment-data
Payment provider data for a payment taken on our side: a type and a set of form fields. If you take the payment yourself, you skip this call.
GET /event-points/{id}/barcodes
A bulk export of every barcode of an event — sold in any channel and unsold. It works only for events your own company organises; on events you sell as a partner it answers 403. A partner takes its own barcodes from the payment response or from GET /orders/{id}.
curl "https://st-pa.tbx.pp.ua/api/v2/event-points/{eventPointId}/barcodes" \
  -H "Authorization: Bearer TOKEN"

In the export response: barcode, placeId, placeName, sectorId, sectorName — one record per ticket.

Through PUT /orders/{id}/status you can ask for only three of them.

created→reserved_to_time→paid→refund·expired — set by the system

Six things that make an integration look broken while everything works as designed.

empty list

The event list comes back empty

Your key only sees events linked to your company, and only those whose dateEndOfSale has not passed yet. Finished ones have their own endpoint, /event-points-archived. If an event you expect is missing, write to us — that is an access setting.

availableCount

A sold-out place does not disappear

It stays in the response with availableCount: 0, so you can close it on your side instead of showing a stale offer. Go by that field, not by whether the place is in the list.

id ≠ eventId

An event and a session are different things

id is one specific session — every request uses it. eventId is the shared identifier of the event across all of its dates: group “event → schedule” by that one.

422

The limits are ours to set

The maximum number of tickets in one order and the maximum number of simultaneously reserved tickets across your whole account are configured on our side. Do not hard-code your own constants: catch 422 and read the response body. Release holds you no longer need right away — while they are alive they eat into your limit.

id format

Identifiers are numeric strings

The request body expects "places": ["456", "765"], not resource URLs. The same goes for eventPoint and priceCategory.

401 · 429

The token and your request rate

401 means the 24 hours are up — request the token again and repeat the call. 429 means there are too many requests: you need a backoff, not a retry loop.

The rest are in the reference; these are enough to build a listing and an event page.

id
The session identifier — every other request uses it
eventId
The shared event id across all of its dates
eventName · description
Name and description, in the language from X-API-Language
poster · media
The poster and the rest of the images
dateStarted · dateEnd
Start and end of the session
dateEndOfSale
When sales stop — after this date the event moves to the archive
venue · venueHall
The venue and the specific hall
city · country
For the geographic filters of your storefront
categories · genres
Classification — maps onto your own reference lists
minPrice · maxPrice
Price range: { amount, currency }
countAvailableTickets
How many tickets are left — enough for a list, without the hall plan
minAgeLimit · maxAgeLimit
Age restrictions
widgetHash
The identifier of our sales widget for this event

Every path starts from https://st-pa.tbx.pp.ua/api/v2.

Catalog
GET /event-pointsThe list of events: filters by date, city, country, category, genre and hall
GET /event-points/{id}A single event
GET /event-points-archivedEvents whose sales have already ended
Hall plan
GET /event-points/{id}/sectorsThe sectors of an event
GET /event-points/{id}/sectors/{sectorId}/placesThe places of one sector
GET /event-points/{id}/placesEvery place of an event with prices, price categories and availability
Orders
POST /ordersCreate an order and reserve the places
GET /orders/{id}The state of an order: total, discount, hold deadline; once paid, the tickets with their barcodes
PUT /orders/{id}/add-placeAdd a place to an order that already exists
PUT /orders/{id}/tickets/{ticketId}/removeRemove a ticket from an order
PUT /orders/{id}/reserveExtend the hold
GET /orders/{id}/payment-dataPayment data from the payment provider — only needed when the payment goes through us
PUT /orders/{id}/statusChange the status of an order
PUT /orders/{id}/tickets/{ticketId}/statusChange the status of a single ticket
GET /event-points/{id}/ordersYour orders for an event, without tickets; filters status and payAt[after]
GET /event-points/{id}/barcodesEvery barcode of an event, sold and unsold — only for events your company organises
Reference lists
GET /countries · /citiesGeography
GET /venues · /venue-hallsVenues and halls
GET /categories · /genresEvent classification
GET /promocodesThe promo codes available to your company
Sales through our widget
GET /widget-ordersOrders placed in the widget on your site
GET /widget-orders-refundsRefunds on those orders
  • Moving to the live API. Two things change: the base URL becomes https://api.ticketcrm.net, and we issue a new API key separately. The staging key does not work in production; the rest of the code stays as it is.
  • Synchronization. A working minimum: the catalog every 5–15 minutes, the reference lists once a day, places on user demand.
  • Webhooks instead of polling. If you need near real time — a new event, an event update, a sale, a refund — give us a URL and we will set up delivery. Polling then stays as a safety net.
  • If something does not add up. Send us the endpoint, the time of the request and the response body — we will look on our side. Address: support@ticketsbox.com.

TicketCRM Public API v2 · the reference opens at the address of the environment itself · support@ticketsbox.com