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.
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.
The set of methods, the formats and the behavior are identical. The address, the data and the key differ.
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.
https://api.ticketcrm.net
Real events, real sales. You move here once the integration has passed on staging.
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.
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.
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.
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.
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.
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.
type and a set of form fields. If you take the payment yourself, you skip this call.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.
Six things that make an integration look broken while everything works as designed.
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.
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 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.
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.
The request body expects "places": ["456", "765"], not resource URLs. The same goes for eventPoint and priceCategory.
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.
X-API-Language{ amount, currency }Every path starts from https://st-pa.tbx.pp.ua/api/v2.
| Catalog | |
| GET /event-points | The list of events: filters by date, city, country, category, genre and hall |
| GET /event-points/{id} | A single event |
| GET /event-points-archived | Events whose sales have already ended |
| Hall plan | |
| GET /event-points/{id}/sectors | The sectors of an event |
| GET /event-points/{id}/sectors/{sectorId}/places | The places of one sector |
| GET /event-points/{id}/places | Every place of an event with prices, price categories and availability |
| Orders | |
| POST /orders | Create 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-place | Add a place to an order that already exists |
| PUT /orders/{id}/tickets/{ticketId}/remove | Remove a ticket from an order |
| PUT /orders/{id}/reserve | Extend the hold |
| GET /orders/{id}/payment-data | Payment data from the payment provider — only needed when the payment goes through us |
| PUT /orders/{id}/status | Change the status of an order |
| PUT /orders/{id}/tickets/{ticketId}/status | Change the status of a single ticket |
| GET /event-points/{id}/orders | Your orders for an event, without tickets; filters status and payAt[after] |
| GET /event-points/{id}/barcodes | Every barcode of an event, sold and unsold — only for events your company organises |
| Reference lists | |
| GET /countries · /cities | Geography |
| GET /venues · /venue-halls | Venues and halls |
| GET /categories · /genres | Event classification |
| GET /promocodes | The promo codes available to your company |
| Sales through our widget | |
| GET /widget-orders | Orders placed in the widget on your site |
| GET /widget-orders-refunds | Refunds on those orders |
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.