laravel-notification-channels/hipchat
Laravel notification channel for sending messages to HipChat rooms via webhooks. It integrates with Laravel’s notification system so apps can deliver room alerts from notification classes, making it useful for legacy HipChat integrations.
Reality check before you install: HipChat was discontinued by Atlassian in 2019, and this channel has been archived since its last release in November 2018. There is no supported HipChat cloud to send to. The only legitimate day-to-day use is maintaining a legacy Laravel app that still talks to an on-prem HipChat Server or an internal proxy that speaks the HipChat v2 API. For anything new, budget the migration to Slack/Teams/Discord instead of adopting this package.
What v2.2.0 changes: very little. The release is a dependency-constraint cleanup that widens the supported Laravel range to 5.7+. It is a maintenance bump on an archived package — it does not unarchive the project, does not restore any HipChat service, and does not add features. Treat it as "the constraints no longer fight you on a 5.7/5.8 app," not as a signal that the package is maintained again.
For the legacy case, the minimal path is:
Install the channel (pin the version; don't float it):
composer require laravel-notification-channels/hipchat:^2.2 --with-all-dependencies
v2.2.0 loosened the constraint web, so a 5.7/5.8 app should resolve cleanly where earlier tags fought over Guzzle/Illuminate versions. Do not read "Laravel 5.7+" as "Laravel 10/11" — read the require block of the v2.2.0 tag before assuming. On anything modern you will still be overriding constraints in a fork branch rather than forcing --ignore-platform-reqs in production.
Register the provider only if you're below Laravel 5.5. On 5.5+ package discovery handles NotificationChannels\HipChat\HipChatServiceProvider. On older apps, add it to config/app.php.
Add credentials to config/services.php (this is the package's config surface — there is no separate publishable config file in most versions):
'hipchat' => [
'token' => env('HIPCHAT_TOKEN'),
],
HIPCHAT_TOKEN=your-hipchat-api-v2-token
The token needs permission to post room notifications (send_notification). Room notification tokens are scoped to a single room; a personal/integration token with broader scope lets you target rooms dynamically.
Look here first:
src/HipChatChannel.php — how the room is resolved (message->room vs. routeNotificationFor...) and the exact API endpoint/base URI it calls.src/HipChatMessage.php — the builder methods actually available (room, message, from, color, notify, format).CouldNotSendNotification) — what gets thrown on non-2xx responses.composer.json at the v2.2.0 tag — the authoritative list of what "5.7+" actually means for your app.services.php key name and builder method names for your pinned version.First use case — smoke test the token and room before writing any notification class. Wire a minimal notification and send it synchronously in tinker:
use NotificationChannels\HipChat\HipChatChannel;
use NotificationChannels\HipChat\HipChatMessage;
use Illuminate\Notifications\Notification;
class DeployFinished extends Notification
{
public function via($notifiable)
{
return [HipChatChannel::class];
}
public function toHipChat($notifiable)
{
return HipChatMessage::create()
->room('ops-alerts')
->from('Deploy Bot')
->message('Deploy #1234 finished')
->color('green');
}
}
If that returns a 2xx and the message lands, everything else is just routing and message composition.
Static room in the message, dynamic room on the notifiable. For team-wide alerts, hard-code ->room() in the notification. For per-user or per-tenant routing, implement the route method on the notifiable and let the channel fall back to it:
class Team extends Model
{
use Notifiable;
public function routeNotificationForHipChat($notification)
{
return $this->hipchat_room_id;
}
}
Confirm precedence in HipChatChannel::send() — typically an explicit ->room() on the message wins, with the notifiable route as fallback. Knowing this prevents "why did it go to the wrong room" bugs.
Severity mapping in a base notification. Wrap the package's builder once so every alert has consistent color/notify semantics:
abstract class HipChatAlert extends Notification implements ShouldQueue
{
protected function alert(string $text, string $severity): HipChatMessage
{
$color = match ($severity) {
'critical' => 'red',
'warning' => 'yellow',
default => 'green',
};
return HipChatMessage::create()
->from(config('app.name'))
->message($text)
->color($color)
->notify($severity === 'critical'); // triggers HipChat notification, not just room history
}
}
On-demand notifications. For ad-hoc messages that aren't tied to a model, use anonymous notifiable routing. Verify the route key against the channel source first — it's usually the channel name (HipChat):
Notification::route('HipChat', 'ops-alerts')->notify(new DeployFinished());
Queue everything, and throttle it. The channel makes a synchronous HTTP call. Implement ShouldQueue, put alerts on a dedicated queue, and rate-limit when you fan out to many rooms:
public function __construct(public string $message) {}
public function via($notifiable) { return [HipChatChannel::class]; }
public function middleware() // Laravel 8+; use ->onQueue('alerts') on older versions
{
return [(new RateLimited('hipchat'))->dontReport()];
}
Guard dev/test environments. The easiest way to spam a real incident room is a local test suite. Configure rooms per environment and short-circuit outside production:
public function via($notifiable)
{
return app()->environment('production') ? [HipChatChannel::class] : [];
}
Testing. Notification::fake() asserts that the channel was selected, not the payload:
Notification::fake();
$team->notify(new DeployFinished());
Notification::assertSentTo($team, DeployFinished::class, function ($notification, $channels) {
return in_array(HipChatChannel::class, $channels, true);
});
v2.2.0 does not change the HTTP layer, so the package still predates Http::fake() and likely uses a Guzzle client directly. You usually cannot assert the outgoing request with Laravel's HTTP fake. Bind a Guzzle MockHandler into the container if the package resolves its client, or do a single integration test against a stub endpoint.
Abstract the channel now. Even if you're keeping the integration alive, define your own AlertChannel interface or a HipChatNotifier service and call that from jobs. When you migrate off HipChat, you change one class instead of hunting every via() method.
composer.json, vendor the source if necessary, and do not build new features on it. The high "opportunity" score here is the opportunity to replace it, not adopt it.composer.json rather than the release note. If you're on Laravel 9+, still expect to override Illuminate/Guzzle constraints in a fork branch — --ignore-platform-reqs in production is not a plan.https://api.hipchat.com/v2/. On-prem HipChat Server or a compatibility proxy needs a different host. Check HipChatChannel/the client class for a configurable base URL; if there isn't one, you'll need a fork or a local proxy that rewrites the host.yellow, green, red, purple, gray, random in the v2 API). An arbitrary hex value is rejected by the API. Keep severity→color mapping in one place.notify is what makes noise. Without it, messages only appear in room history. Mentioning users requires notify(true) and the right message format plus valid user IDs (@UserId), not display names.services.hipchat block, run php artisan config:cache in deployed environments. A stale config cache producing "token is null" is the most common self-inflicted failure here.Notification::fake() lies about payloads. It won't catch a malformed room or a color the API rejects. Keep one real end-to-end smoke test against a staging room.color → attachment color or emoji prefix, notify → <!channel> / <!here> or user mentions, from → attachment author or bot username. Write the mapping table before you start so behavior stays equivalent during cutover.HipChatMessage to add defaults, wrap the channel in your own notification channel class implementing send(), or replace the package entirely with a ~40-line channel built on Laravel's HTTP client pointing at whatever incident tool you actually use. Given the package's state, the third option is usually the cheapest long-term move.How can I help you explore Laravel packages today?