The BrightStar REST API gives you programmatic access to events, tickets, orders, and attendees — the same data behind BrightStar's own dashboard, reachable through code instead of a browser. Every endpoint speaks JSON, and every request has to be authenticated, so before you build anything against it you need an API key and a plan for where each call fits into your own systems. That might be a check-in app pulling the attendee list moments before doors open at a kirtan night, a script pulling recent orders for reconciliation after a retreat closes registration, or a custom form that creates an event and then reads back its ticket types. All of it starts at the same base URL and passes through the same authentication check before it touches any real data.
Authenticating requests
Every call to https://api.brightstarevents.com/v1 needs an Authorization header in the form Bearer {your_api_key}, plus a Content-Type of application/json. BrightStar keeps only a hash of your key, never the key itself — it's shown to you once, when it's created, and after that the API can recognize it but not reveal it. That matters if a database is ever exposed: a leaked hash doesn't produce a working credential.
Revoking a key doesn't delete its record, it marks it revoked, and because every request has to match against an unrevoked key, the change takes effect on the very next call — there's no delay where an old key keeps working. The check is fail-closed throughout: a malformed key, a revoked key, and even an error looking one up all resolve to no access, never to access by default. A bad or missing key returns HTTP 401; a valid key that isn't permitted to do what you're asking returns 403.
Creating and managing events
The events endpoints follow a listing's lifecycle. GET /v1/events lists what exists, with status, limit, offset and sort parameters, so you can pull just your published events or just your drafts. GET /v1/events/{event_id} fetches one in full, POST /v1/events creates a new one from a body with title, description, start_date and the rest of a listing's fields, and PATCH /v1/events/{event_id} updates only the fields you send.
DELETE /v1/events/{event_id} only works on a draft. Once an event moves past draft, tickets may already be selling against it, which means there could be real orders and real attendees riding on it — deleting it outright would orphan all of that. So the delete endpoint is deliberately limited to the one stage where nothing has been sold yet.
Tickets, orders, and your attendee list
Ticket types live under an event: GET /v1/events/{event_id}/tickets lists them, POST /v1/events/{event_id}/tickets creates one with a name, price and quantity, and PATCH /v1/tickets/{ticket_type_id} updates an existing type. Orders sit a level up — GET /v1/events/{event_id}/orders lists them with status and date filters, GET /v1/orders/{order_id} returns one in full, POST /v1/orders/{order_id}/refund issues a refund with an optional amount and a reason, and POST /v1/orders/{order_id}/resend resends the order confirmation to whoever bought it.
Attendees have their own endpoint, GET /v1/events/{event_id}/attendees, filterable by ticket_type and checked_in — because a buyer and an attendee aren't always the same person; one order can hold several tickets, each belonging to someone different. That's the endpoint to call for a door list, not the orders endpoint, when what you need is who is actually walking in.
How pagination keeps large lists manageable
Any endpoint that returns a list takes limit and offset parameters. limit controls how many results come back, defaulting to 20 and capping at 100; offset tells the API how many results to skip before it starts counting. GET /v1/events?limit=20&offset=40 returns results 41 through 60. The headers X-Total-Count, X-Page and X-Per-Page tell you how many results exist in total and where the current page sits, so your code knows when to stop asking.
Ordering stays stable even when two records were created in the same second, since results sort by creation time with the record's own ID as a tiebreaker — a page boundary landing between near-simultaneous records never skips one or returns it twice. There's also a cursor mode for pulling a full history rather than a snapshot: give it a starting timestamp and results come back oldest-first from there, and advancing the cursor to the last record's creation time each round walks the whole history without gaps.
Reading error responses
Every error comes back in the same shape — a JSON object with an error field holding a code, a human-readable message, and, where relevant, the specific field that caused the problem, like a missing title. The HTTP status tells you the category before you even open the body: 200 and 201 mean success, 400 means the input was invalid, 401 means the key was missing or wrong, 403 means the key is valid but not permitted to do that particular thing, 404 means the resource doesn't exist, 429 means you're being rate limited, and 500 means something broke on BrightStar's side. Checking the status code first and the error code second is usually enough to route a failure automatically instead of surfacing a raw response to a person.