themusicdev / eventbrite
Eventbrite API v3 client - PSR-18 HTTP, PSR-16 cache, framework agnostic
Requires
- php: ^8.2
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.0 || ^2.0
- psr/log: ^3.0
- psr/simple-cache: ^3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.9
- guzzlehttp/psr7: ^2.7
- pestphp/pest: ^3.0
- phpstan/phpstan: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Eventbrite API v3 client — PSR-18 HTTP, PSR-16 cache, framework agnostic MIT code.
A small client for the Eventbrite API v3: read events, attendees, orders and ticket classes, manage webhooks, and run the OAuth authorization-code flow — with no framework lock-in.
Why this package exists: the official eventbrite/eventbrite-sdk-php has
had no release since 2019, and the popular community client is abandoned —
modern PHP (8.2+) had no maintained, framework-agnostic Eventbrite client.
- PSR-18 HTTP client in (Guzzle, Symfony HTTP Client, WordPress-anything)
- PSR-16 cache optional — GET responses cached per token (10 min default)
- PSR-3 logger optional — warnings on 429 backoff
- Continuation pagination exposed as generators
- Typed exceptions carrying status code + Eventbrite's error body
- 429 retries with exponential backoff (1s, 2s, 4s, then throws)
- Webhook helpers: list, create, ensure (idempotent reconcile), delete
Install
composer require themusicdev/eventbrite
Requirements: PHP 8.2+, a PSR-18 client and PSR-17 factories. With Guzzle:
composer require guzzlehttp/guzzle
Getting an API key
- Create/Sign in to an Eventbrite account → account settings → Developer Links → Developer Portal → Get a Free API Key.
- That page shows:
- API key — the OAuth
client_id - Client secret — for OAuth server-side code exchange
- Private token — a personal bearer token for your own account (handy to start)
- API key — the OAuth
- Full walkthrough: docs/getting-an-api-key.md.
Quickstart — private token
use GuzzleHttp\Client as HttpClient; use GuzzleHttp\Psr7\HttpFactory; use TheMusicDev\Eventbrite\EventbriteClient; $httpFactory = new HttpFactory(); // PSR-17 request + stream factory $client = new EventbriteClient( new HttpClient(), $httpFactory, $httpFactory, $_ENV['EVENTBRITE_TOKEN'], // never hard-code; see docs/getting-an-api-key.md ); $me = $client->me(); // GET /users/me/ $organizations = $client->organizations(); // GET /users/me/organizations/ $orgId = $organizations['organizations'][0]['id'] ?? throw new RuntimeException('This Eventbrite account has no organization.'); $allEvents = $client->organizationEvents($orgId); // Generator over pages foreach ($allEvents as $event) { // ... }
organizations() can come back empty. Eventbrite's docs
say the user id then works as the organization id, but it returned 404 in testing
(2026-11-05), so create an organization in the Eventbrite dashboard first.
Caching
Pass any PSR-16 cache; GET responses cache per token for 600s (configurable
$cacheTtl, 0 disables). The webhooks listing is never cached (it is the
enabled-state source of truth). With symfony/cache:
use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Symfony\Component\Cache\Psr16Adapter; $cache = new Psr16Adapter(new FilesystemAdapter('eventbrite', 600)); $client = new EventbriteClient(new HttpClient(), $httpFactory, $httpFactory, $token, $cache);
Query the API
Every response method returns decoded JSON arrays; non-2xx throws
EventbriteApiException (getStatusCode(), getErrorBody()).
try { $client->event('123456'); } catch (EventbriteApiException $e) { // $e->getStatusCode(); $e->getErrorBody(); } // one page: GET /organizations/{id}/events/ with any query params $page = $client->get('organizations/' . $orgId . '/events/', ['time_filter' => 'all']); // paginated generators (continuation-based) $attendees = $client->eventAttendees('123456'); $orders = $client->eventOrders('123456'); foreach ($attendees as $attendee) { /* attendee['profile']['email'], ['affiliate'] ... */ }
Webhooks
// list (never cached) $hooks = $client->webhooks($orgId)['webhooks']; // ensure an endpoint has one webhook per action — idempotent, run freely $client->ensureWebhooks($orgId, 'https://example.com/webhook', [ 'attendee.updated', 'barcode.checked_in', 'order.placed', 'event.published', ]); // remove the ones pointing at an endpoint $client->disableWebhooks($orgId, 'https://example.com/webhook');
Deliveries POST {"api_url": ".../v3/orders/123/"} to your endpoint — fetch the
pointed object with fetchUrl() (bypasses the cache, so you never store a
pre-change snapshot) and upsert. Validate the URL: Eventbrite webhooks carry no
signature, so your receiver must refuse anything outside
https://www.eventbriteapi.com/v3/. Never return 410 — it auto-unsubscribes
your endpoint.
OAuth (authorization code flow)
use TheMusicDev\Eventbrite\EventbriteOAuth; $oauth = new EventbriteOAuth($httpClient, $httpFactory, $httpFactory, $clientId, $clientSecret, // API key page 'https://example.com/oauth/eventbrite/callback', // registered redirect URI $appSecret, // any stable app secret, signs the state ); session_start(); // 1. redirect the browser to: $oauth->authorizeUrl($oauth->newState(session_id())); // 2. on the callback (?code=...&state=...): if ($oauth->verifyState($state, session_id())) { $tokenResponse = $oauth->exchangeCode($code); // ['access_token' => ...] // store $tokenResponse['access_token'] and pass it to the client }
The state is signed and stored nowhere, but it is bound to the visitor value, so a state minted for one browser fails in another. That blocks login CSRF, where an attacker gets a victim to open the attacker's own callback URL. Any value that identifies the browser works, as long as it survives until the callback:
- Don't regenerate the session id mid-flow.
SameSite=Strictsession cookies aren't sent on the redirect back from Eventbrite; useLax.
States expire after 10 minutes ($maxAge on verifyState() changes this) and are
not single-use.
Testing your integration
tests/Support/FakeHttp.php is a recording, rotating PSR-18 client + PSR-17
factories in one object. Composer doesn't autoload it for consumers, so require
it from vendor/themusicdev/eventbrite/tests/Support/FakeHttp.php or copy it, and
run composer require --dev guzzlehttp/psr7, which it needs. See tests/ for usage.
Docs
- Getting an API key
- API v3 reference — the parameter lists this client's methods were verified against
Quality
- PHPStan (max + strict-rules), PHP-CS-Fixer (PSR-12), Pest tests + coverage on CI (PHP 8.2–8.4)
-
composer-require-checkerguards the PSR-only dependency list