Building an HTTP Connector
An HTTP Connector is any HTTPS web service that implements the Spektrix Connect endpoints. You can build it in any language and host it anywhere that Spektrix can reach over the public internet. Spektrix only cares about the HTTP contract described here and in the endpoint reference.
The Spektrix Connect interface
Spektrix talks to every source system through the Spektrix Connect interface. Your connector is the HTTP implementation of that interface. Each operation Spektrix needs maps to one endpoint, so you need to implement every endpoint in the table below.
| Operation | Endpoint | When Spektrix calls it |
|---|---|---|
| List instances | GET instances | Start of every import |
| Get events | GET events | Import |
| Get ticket types | GET ticket-types | Import and mapping |
| Get price bands | GET price-bands | Import and mapping |
| Get venue | GET venues/{venueId} | Import |
| Get seating plan | GET plans/{seatingPlanId} | Import |
| Get instance summaries | GET instances/status | Listing availability and prices of many instances |
| Get instance status | GET instances/{instanceId}/status | Showing a seating plan, and while adding seats |
| Create basket | POST baskets | First ticket from your source system in a Spektrix basket |
| Get basket | GET baskets/{basketId} | After every change to a basket |
| Add reserved tickets | POST baskets/{basketId}/reserved-tickets | Seats selected on a seating plan |
| Add unreserved tickets | POST baskets/{basketId}/unreserved-tickets | Tickets added for an unreserved area |
| Change ticket types | PATCH baskets/{basketId}/tickets | Ticket type changed in the basket |
| Remove tickets | DELETE baskets/{basketId}/tickets | Tickets removed from the basket |
| Set promo code | PATCH baskets/{basketId}/promo-code | Promo code entered or cleared |
| Set agent customer | PATCH baskets/{basketId}/agent-customer | Just before confirmation |
| Confirm basket | POST baskets/{basketId}/confirm | After the customer has paid in Spektrix |
There's no separate price list or basket keep-alive endpoint. Prices come from the availability endpoints, and baskets are read through get basket.
Rules for every endpoint
These rules apply to every endpoint, so they aren't repeated in the endpoint reference.
URLs
- Every endpoint path is appended to the base URL configured in Spektrix.
https://connector.example.com/v1andhttps://connector.example.com/v1/behave the same. - Path parameters such as
{basketId}are URL-encoded. - Lists of ids in a query string (
ids,ticketIds) are sent as one comma-separated, URL-encoded value, not as repeated parameters. For example,events?ids=1AEBCDFG,2AEBCDFG.
Authentication
Every request includes the authentication header configured in Spektrix. Reject requests where the header is missing or wrong with 401 Unauthorized. See Authentication and setup.
JSON
- Request and response bodies are JSON.
- Requests with a body are sent as
Content-Type: application/json; charset=utf-8, with camelCase property names. - Responses are read case-insensitively, and numbers may be returned as JSON strings, such as
"35.00". - Where a response contains different kinds of seating area, each area includes a
typeproperty (Group,ReservedorUnreserved). It doesn't have to be the first property. - If an endpoint returns no content, Spektrix doesn't read the response body. For those endpoints, return
204 No Contentor an empty200 OK.
Dates and times
Dates and times are local times at the venue, in the form 2026-03-01T19:30:00. Spektrix ignores any time zone offset or Z suffix in your response, so don't convert times to UTC.
Ids
Every id is an opaque string owned by your connector. Spektrix stores ids and sends them back to you without interpreting them. Ids must:
- stay the same over time, because Spektrix stores them during the import and uses them for months afterwards.
- be unique within their type across your connector. In particular, seat ids must be unique across a whole seating plan, not just within an area.
You can use the source system's own ids directly, or build your own. For example, if the source system identifies a seat only by area, row and number, you could use stalls~A~1.
Money
Amounts are decimal numbers in the source system's currency, which must match the Spektrix client's currency. Each price is the full amount the agent pays the source system for the ticket, including any fees or discounts.
Timeouts
Spektrix abandons any request that hasn't completed within 5 seconds, and the action that triggered it fails. A customer is usually waiting on the other end, so every endpoint should respond well within that limit.
If the source system's agency API is slow, cache data that changes rarely, such as ticket types, price bands, venues and seating plans.
Errors
Spektrix treats any 2xx status as success. Any other status fails the call.
Where possible, return errors as RFC 9457 problem details with Content-Type: application/problem+json. Spektrix logs the detail property, so include enough information to diagnose the problem:
HTTP/1.1 502 Bad Gateway
Content-Type: application/problem+json
{
"title": "Source system unavailable",
"detail": "The agency API returned 503 for GET /agency/v2/performances/88123/seats."
}
Some endpoints document specific errors that Spektrix handles differently. Spektrix identifies them by both the HTTP status and a stable type URI. Currently there's one:
| Status | type | Endpoint | Meaning |
|---|---|---|---|
409 | https://integrate.spektrix.com/problems/tickets-unavailable | Add reserved tickets | One or more requested seats are no longer available |
For any other failure, Spektrix shows staff or the customer a general message saying that the source system couldn't complete the request.
All-or-nothing changes
Requests that add tickets contain a list of tickets. Treat each request as a single unit: either every ticket is added, or none are and the request fails. Never leave a basket half-changed after a failed request.
Basket lifetime
Spektrix doesn't call a keep-alive endpoint. A Spektrix basket can stay open while the customer browses and pays, so make sure the matching basket in the source system stays alive at least that long. For example, you could use the longest reservation timeout the source system allows, or extend the reservation whenever your connector receives a request for that basket.
Reprices
Changing one ticket in a basket, such as adding a ticket or applying a promo code, can change the price of other tickets if the source system applies offers. Spektrix calls get basket after every change and updates every ticket's price from the total you return, so always return the current price for each ticket.
Example
The example below is a minimal connector skeleton using ASP.NET Core minimal APIs. It shows the authentication check, error handling and two endpoints translating to a fictional agency API. IAgencyApiClient stands for whatever client library you use to call the source system.
using System.Security.Cryptography;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
builder.Services.AddHttpClient<IAgencyApiClient, AgencyApiClient>(); // calls the source system with its own credentials
var app = builder.Build();
// ① Authenticate Spektrix: the header name and value match those entered in Spektrix Settings.
var headerName = builder.Configuration["SpektrixConnect:AuthHeaderName"]; // e.g. "X-Api-Key"
var headerValue = builder.Configuration["SpektrixConnect:AuthHeaderValue"]; // from a secret store
app.Use(async (context, next) =>
{
var supplied = context.Request.Headers[headerName].ToString();
if (!CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(supplied), Encoding.UTF8.GetBytes(headerValue)))
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return;
}
await next();
});
var connect = app.MapGroup("/v1");
// GET ticket-types: translate the source system's ticket categories into Spektrix Connect ticket types.
connect.MapGet("/ticket-types", async (IAgencyApiClient agency) =>
{
var categories = await agency.GetTicketCategoriesAsync();
return categories.Select(c => new { id = c.Code, name = c.DisplayName });
});
// POST baskets/{basketId}/reserved-tickets: hold every requested seat, or none of them.
connect.MapPost("/baskets/{basketId}/reserved-tickets", async (string basketId, NewReservedTicket[] tickets, IAgencyApiClient agency) =>
{
var result = await agency.HoldSeatsAsync(
cartId: basketId,
seats: tickets.Select(t => new AgencySeatHold(t.Instance, t.Seat, t.Type)));
if (result.UnavailableSeatIds.Any())
{
return Results.Problem(
statusCode: StatusCodes.Status409Conflict,
type: "https://integrate.spektrix.com/problems/tickets-unavailable",
title: "Tickets unavailable",
extensions: new Dictionary<string, object?>
{
["data"] = new { tickets = result.UnavailableSeatIds.Select(id => new { seatId = id }) }
});
}
return Results.NoContent();
});
app.Run();
record NewReservedTicket(string Instance, string Seat, string Type);
Because the base URL configured in Spektrix would be https://connector.example.com/v1, MapGroup("/v1") makes the paths line up with the endpoint reference.
Checklist
Before you give the connection details to a Spektrix client, check that:
- Every endpoint in the endpoint reference is implemented.
- The connector is served over HTTPS at a stable base URL.
- Requests without the correct authentication header get
401 Unauthorized. - The source system's credentials are stored securely and aren't sent to Spektrix.
- Every endpoint responds well within 5 seconds.
- Ids are stable, and seat ids are unique across a seating plan.
- Dates are local venue times, and amounts are in the Spektrix client's currency.
- Adding tickets is all-or-nothing, and unavailable seats return the documented
409error. - Get basket always returns each ticket's current price.
- Confirming the same basket twice fails, rather than creating a second order.