Skip to main content

Upgrading from 1.x

2.0.0 changes one thing: the root namespace moves from Webhooks\ to Pushery\Webhooks\, matching every other package in the family (Pushery\WireKit\, Pushery\LegalConsent\).

No behavior changes, no configuration keys move, no table changes. Every class keeps its name and its position; only the root in front of it is different. The Composer package name is unchanged — pushery/webhooks-for-laravel.

The change, in one line

-use Webhooks\Server\PendingWebhook;
+use Pushery\Webhooks\Server\PendingWebhook;

For most applications a search and replace over use Webhooks\ and \Webhooks\ is the whole upgrade. The rest of this page is the part that search and replace does not reach.

1. Drain the queue before you deploy

This is the one that bites, and it bites in production rather than in CI.

A queued job is stored as a serialized payload carrying its own class name. Any Webhooks\Server\Jobs\CallWebhookJob or Webhooks\Client\Jobs\ProcessWebhookJob still waiting when the new code goes live cannot be unserialized — the class it names no longer exists, and the worker fails the job rather than running it.

# Before deploying: let the queue empty, or stop enqueueing and wait it out.
php artisan queue:monitor webhooks

Check failed_jobs too. A failed job retried after the upgrade hits the same wall, so retry anything you care about before you deploy, or accept that those entries are no longer replayable and prune them.

Deliveries themselves are safe: a delivery is a row, not a job, and rows carry no class name (see below). Only the in-flight job wrapper is affected.

2. Fix bootstrap/providers.php by hand

The search and replace above does not reach this file, and it is the one that stops the application from booting at all.

Four providers are auto-discovered from composer.json and move with the package. Four are not, and you register those yourself — so check every one of these four lines, not only the layers you remember switching on:

// bootstrap/providers.php
Webhooks\Dashboard\WebhooksDashboardServiceProvider::class, // the observability dashboard
Webhooks\Platform\SelfServicePortalServiceProvider::class, // the self-service portal
Webhooks\WebhooksUiServiceProvider::class, // the operator console
Webhooks\Pulse\WebhookPulseServiceProvider::class, // the Laravel Pulse card

A provider list holds a bare class name — no use, no leading backslash — so neither use Webhooks\ nor \Webhooks\ matches it. Prefix the ones you have:

-Webhooks\Dashboard\WebhooksDashboardServiceProvider::class,
+Pushery\Webhooks\Dashboard\WebhooksDashboardServiceProvider::class,

Miss it and the application does not start: Laravel resolves the provider list on every request, and a class name that no longer exists is a fatal on boot rather than a feature that quietly stops working. If you registered none of the three, this step is empty.

3. Re-publish anything you published

Published copies live in your application and still carry the old imports. Re-run the tags you use, or edit the imports by hand:

php artisan vendor:publish --tag=webhooks-migrations --force
php artisan vendor:publish --tag=webhooks-client-migrations --force
php artisan vendor:publish --tag=webhooks-server-migrations --force
php artisan vendor:publish --tag=webhooks-dashboard-migrations --force
php artisan vendor:publish --tag=webhooks-dashboard-views --force
php artisan vendor:publish --tag=webhooks-self-service-views --force
php artisan vendor:publish --tag=webhooks-views --force

# The operator console — ONE of these two, whichever variant you published.
# They write to the same destination, so running both leaves you with the second.
php artisan vendor:publish --tag=webhooks-ui --force
php artisan vendor:publish --tag=webhooks-ui-wirekit --force
warning
--force overwrites your edits

If you have customized a published view or migration, do not use --force. Fix it in place instead.

For a published view there is one more edit than the imports, and it is easy to miss because nothing about it looks like a namespace:

- wire:click="delete({{ $subscription->id }})"
+ wire:click="destroy({{ $subscription->id }})"

delete() still exists and still works, so a published copy that keeps calling it is not broken by this release. What it already was, before 2.0, is broken under a strict Content-Security-Policy: without unsafe-eval Livewire parses wire: expressions with its own parser, and delete is a keyword there — the expression never was a call, so the button rendered, the confirmation appeared, and nothing happened. That is why the method was renamed. Change the one line, or re-publish the view, and the button works under a CSP too.

A published migration that already ran does not need to run again. Its imports still have to be corrected, or the file fails to load the next time Laravel scans the directory.

The Livewire component aliases are unchanged (webhooks.self-service.endpoint-list and the rest are plain strings, not class names), so a view that mounts a panel by alias needs no edit at all.

4. Clear every cache that stores a resolved class name

php artisan config:clear
php artisan route:clear
php artisan optimize:clear

A cached configuration holds the resolved signature-scheme class, a cached route table holds the controller class. Both name the old root and neither is refreshed by composer update. If you rebuild caches in your deploy, make sure the clear happens after the new code is in place.

5. Update config values that name a package class

Only if you set them — each defaults to a package class that the new version resolves for you. Look for these keys in your published config/webhooks.php:

KeyNames a class when you set it
core.signing.schemea SignatureScheme
client.configs.*.schemea SignatureScheme
client.configs.*.profilea WebhookProfile
client.configs.*.responsea RespondsToWebhook
client.configs.*.modela WebhookCall subclass
client.configs.*.dedupe_ida DedupeKeyResolver
dashboard.source_modela WebhookDelivery subclass

A class of your own in any of these — a custom verifier, your own job — is unaffected. Only names starting Webhooks\ move.

6. Your tests

Type hints, expect(...)->toBeInstanceOf(...), Event::assertDispatched(...), mocks and Bus::assertDispatched(...) all name package classes. The same search and replace covers them.

What you do NOT have to do

No database migration. No stored row holds a package class name — verified rather than assumed:

  • The package registers no morph map, and owner_type on webhook_subscriptions and webhook_deliveries holds your model (App\Models\User, App\Models\Team), never a package class.
  • No shipped migration writes a class name into a column.
  • event_type holds your event strings; status holds an enum value, not its class.

So the delivery log, the subscriptions and their secrets all survive the upgrade untouched. The only place a package class name is stored anywhere is a pending queue payload, which is why draining the queue is step one and the only step with a deadline.

Why the namespace moved at all

Every other package in this family is namespaced Pushery\…, and this one was the exception. A consuming application wrote use Pushery\Webhooks\Enums\DeliveryStatus; from memory, got a class-not-found at runtime, and was right about the convention and wrong about this package. Making the odd one out match is worth one breaking release; leaving it is a tax on every developer who knows the rest of the family.