laravel-notification-channels/pushwoosh
Send push notifications through Pushwoosh directly from your Laravel app using the native notification system. This channel lets you deliver messages to iOS and Android devices via Laravel notifications, keeping your code clean and consistent with other channels.
Install the channel and wire up credentials before touching any notification class:
composer require laravel-notification-channels/pushwoosh
As of v4.0.0 the package supports Laravel 13. If you're on an older Laravel, pin to the v3 line (^3.6) — v4 is a major bump, so check the illuminate/* constraints in the package's composer.json before upgrading an existing app, and run a full test pass afterwards even though the release notes advertise only framework support and no API changes.
Add the Pushwoosh app credentials to config/services.php (this package follows the convention of reading from services.php, not a dedicated config file you publish):
'pushwoosh' => [
'app_id' => env('PUSHWOOSH_APP_ID'),
'api_token' => env('PUSHWOOSH_API_TOKEN'),
],
Then set PUSHWOOSH_APP_ID and PUSHWOOSH_API_TOKEN in .env. The API token is the server token from the Pushwoosh control panel (Settings → API access), not the app code — mixing these up is the most common first-day failure.
The first use case is almost always transactional: a notification class that sends one push to one user's registered device(s).
use NotificationChannels\Pushwoosh\PushwooshChannel;
use NotificationChannels\Pushwoosh\PushwooshMessage;
class OrderShipped extends Notification
{
public function via($notifiable)
{
return [PushwooshChannel::class];
}
public function toPushwoosh($notifiable)
{
return PushwooshMessage::create('Your order is on its way')
->setTitle('Order shipped');
}
}
For targeting, the channel resolves recipients through a routeNotificationForPushwoosh() method on the notifiable (falling back to a device token / Pushwoosh user id field if the package defines one). Start by implementing that method explicitly on your User model:
public function routeNotificationForPushwoosh()
{
return $this->pushwoosh_user_id ?: $this->deviceTokens()->pluck('token');
}
If you can't get a push delivered in the first ten minutes, verify the recipient resolution before debugging the API — a null route is the usual culprit, and this package will not throw loudly for an empty recipient list.
Single-recipient notifications. The standard pattern: via() returns [PushwooshChannel::class], toPushwoosh() returns a PushwooshMessage. Keep one message class per notification so the payload stays readable.
On-demand pushes. Use Notification::route('pushwoosh', $tokenOrUserId)->notify(new SomeNotification) for one-off sends (broadcast to a support device, an internal alert) where there's no notifiable model. This is the cleanest way to test the channel end-to-end without polluting user models.
Payload/custom data. Pushwoosh messages carry a data payload alongside content. Use the message builder's data/parameter setters to attach deep-link keys, an entity id, or a badge count, and handle them client-side:
return PushwooshMessage::create('New message')
->setTitle('Inbox')
->setData(['type' => 'message', 'id' => $message->id]);
Keep data values scalar and small — Pushwoosh rejects or truncates oversized payloads, and nested arrays behave inconsistently across platforms.
Queuing. The channel performs a synchronous HTTP call. Always pair it with a queued notification (implements ShouldQueue or ->onQueue()), or you'll block the request on Pushwoosh latency. Because it's queue-friendly, this composes naturally with transactional flows: fire the notification from a listener/job, not from the controller.
Fan-out with fallbacks. A common production pattern is via() returning [PushwooshChannel::class, MailChannel::class], where push is the fast path and mail is the fallback. Since Pushwoosh failures are typically silent (see below), the mail fallback carries a lot of the reliability weight.
Segments and broadcast. For user-segment sends, the cleaner pattern is to do segment targeting with Pushwoosh's own API/console and reserve this package for per-user transactional pushes. Driving segment broadcasts through a Laravel notification ends up fighting the notifiable abstraction and duplicating recipient lists.
Testing. Use Notification::fake() and assert PushwooshChannel::class was included in the channels, then assert message contents with assertSentTo(...). This is the only reliable way to test the channel without hitting the live API, since there's no first-class fake client shipped with the package.
Upgrading to v4. Treat this as a dependency-constraint change plus a framework-support change, not a rewrite: bump the constraint, confirm your app's Laravel/PHP versions satisfy the new requirements, re-run the notification test suite (channel inclusion + message assertions), and do one real send per platform in staging before shipping. No message-builder or channel-API changes are called out in the v4.0.0 notes.
storage/logs/laravel.log first, then confirm request/response by temporarily logging the HTTP client or by enabling HTTP client events (Http::globalRequestMiddleware). If a push "doesn't arrive," the channel almost never tells you why.401/403 from api.pushwoosh.com is virtually always a token mismatch.devices (hwid push tokens) from users (your own user id). Passing the wrong one yields a 200 with zero delivered. Decide up front whether you key on device tokens or Pushwoosh user ids, and be consistent in routeNotificationForPushwoosh().illuminate/* constraints. Pin the version in composer.json, don't blanket-allow *, and read the require block before upgrading. The public surface is still thin, so when in doubt read src/PushwooshMessage.php rather than guessing at setters.PushwooshChannel::class in via() for your own class that wraps Http::post('https://api.pushwoosh.com/json/1.3/createMessage', ...).200 and an actual device delivery are different events. Don't build retry logic on the HTTP response alone; treat the send as fire-and-forget and make the user-facing state recoverable (e.g., the in-app inbox shows the same content).How can I help you explore Laravel packages today?