jeffersongoncalves/laravel-demio
Laravel Demio is a clean integration for the Demio webinar platform, providing a fluent wrapper around the Demio API to create and manage webinars, register participants, track attendance, and automate event workflows directly from your Laravel application.
composer require the package, then publish its config:
php artisan vendor:publish --tag=demio-config
Add credentials to .env (never commit them) and mirror them in config/demio.php:
DEMIO_API_KEY=...
DEMIO_API_SECRET=...
DEMIO_BASE_URL=https://my.demio.com/api/v1
DEMIO_TIMEOUT=10
Where to look first: config/demio.php for the four knobs, then the DemioClient class itself — at 1.0.0 there is no facade, no service provider documented behaviour beyond config merging, and no README-level docs for response shapes. Read the source of events(), eventDate(), and register() before wiring them into anything, because the return types (array vs. collection vs. response object) are the single biggest unknown you'll hit.
First use case: a connectivity/correctness smoke test from Tinker before building anything:
php artisan tinker
>>> app(\Vendor\Demio\DemioClient::class)->ping();
If ping() returns cleanly you've proven the key, secret, base URL, and outbound TLS all work. That's your fastest way to isolate "credentials wrong" from "code wrong" for the rest of the project. From there, the canonical first feature is: events() to populate a select list → event($id) for detail → eventDate($id, $dateId) for the specific session you want people to register against → register() for signups → participants() for the attendee report.
Inject the client, don't new it. Resolve it from the container in constructors so it's swappable in tests. If the package doesn't register a singleton binding, add one in your own AppServiceProvider:
$this->app->singleton(DemioClient::class, fn () => new DemioClient(config('demio')));
Wrap it in a thin application service. This is the pattern that pays off most. Your controllers and jobs should never touch DemioClient directly — put a DemioService in front of it that owns caching, retries, and error translation:
class DemioService
{
public function __construct(private DemioClient $client) {}
public function upcomingEvents(): array
{
return Cache::remember('demio.events', now()->addMinutes(10), fn () => $this->client->events());
}
public function registerAttendee(int $dateId, array $attendee): array
{
return $this->client->register($dateId, $attendee);
}
}
Cache the read-only lookups. Events and event dates change rarely; fetching them on every page render is the classic first mistake. events(), event(), and eventDate() are cache candidates. participants() is not — it's live data and is usually the most expensive call.
Push participants() into a queued job. Attendee lists for large webinars can be slow and can blow past your timeout. Fetch them in a job, store into your own table, and render from there. This also gives you a natural retry boundary.
Model the registration form yourself. register() takes attendee data; map your validated FormRequest payload into exactly the fields Demio expects and keep that mapping in one place. Demio is strict about what it receives, and a missing custom field will fail silently in your logs rather than throwing.
Handle DemioAuthenticationException distinctly. It's the only typed failure the package gives you, and it is almost always a deployment problem, not a user problem:
try {
$service->upcomingEvents();
} catch (DemioAuthenticationException $e) {
report($e);
Log::critical('Demio credentials rejected — check DEMIO_API_KEY/SECRET');
return collect();
}
Testing. If the client is built on Laravel's Http client, Http::fake() will intercept everything and let you assert outgoing URLs and payloads — verify this early by faking a 401 and confirming the exception fires. Otherwise, bind a stub DemioClient into the container in your test case.
timeout units and magnitude. It's almost certainly seconds, but the default is not stated. ping() is a fine test at 5–10s; participants() for a large event may not be. Raise the timeout for that call specifically rather than globally.Http::retry() in a wrapper, or a queued job with $backoff, is the cheap fix.base_url is your escape hatch. Point it at a proxy, a mock server, or a recording of real responses for integration tests. Don't hardcode Demio URLs anywhere in your code.events() and participants() may return a truncated first page. Before trusting a participant export, compare a count against the Demio UI. If it's paginated, you'll need to loop the client yourself since there are no cursor helpers.app(DemioClient::class) fails to resolve, it needs constructor arguments that aren't auto-wirable from config — bind it yourself as shown above.config/demio.php or the env vars in production, run php artisan config:cache (or config:clear). Stale config cache is the number one cause of "it works locally" auth failures.participants() on a schedule or keep the trigger on your side at the point you call register().^1.0. This is a fresh initial release with a thin API surface; expect method signatures and response shapes to shift in minor releases. Read the changelog before every composer update.How can I help you explore Laravel packages today?