jeremykenedy/slack-laravel
Slack integration for Laravel: send messages through incoming webhooks or the Slack Web API, route logs to Slack channels, and build notifications with a simple, configurable wrapper. Includes channel helpers and clean error handling for fast setup.
Install the package on a Laravel app that matches its support matrix (v2.6.0 targets Laravel 8; older Laravel 6/7 should pin ^2.5). Confirm PHP version and illuminate/* constraints in the package composer.json before adding it:
composer require jeremykenedy/slack-laravel
Publish the config so you get config/slack.php and can see the exact webhook/channel keys the package actually uses:
php artisan vendor:publish --provider="JeremyKenedy\\Slack\\SlackServiceProvider"
Set the webhook in .env, not in the config file. Create a Slack incoming webhook for a dedicated #alerts (or per-app) channel, then:
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/XXX/YYY/ZZZ
SLACK_CHANNEL=#alerts
Re-run php artisan config:cache on cached deployments.
Where to look first: the package README for the exact method signatures (send, attachments/blocks), and the published config/slack.php for key names (webhook, channel, username, icon). The package is small — the provider and a single client class are worth a quick read before you build on it.
First use case: don't wire it into production code first. Prove the round-trip from a console command or tinker:
php artisan tinker
>>> \Slack::send('slack-laravel smoke test');
Confirm the message lands in the expected channel, then wrap it into one low-risk path (e.g. a queued notification from the exception handler) and expand.
Wrap it in your own notifier class. Instead of scattering Slack::send(...) calls across controllers, inject a small App\Notifications\SlackNotifier that owns message formatting, channel selection, and try/catch. It keeps the vendor API in one place and makes future package bumps a one-file change.
class SlackNotifier
{
public function __construct(private Slack $slack) {}
public function alert(string $text, array $context = []): void
{
try {
$this->slack->send($text, $context);
} catch (\Throwable $e) {
report($e); // never let Slack failure break the request
}
}
}
Prefer the facade or container binding based on context. Facades are convenient in controllers and console commands; constructor injection (or app(Slack::class)) is cleaner in services and tests because you can swap a fake.
Use attachments / rich payloads for structured alerts. For alert-style messages (deployments, exception digests, cron failures), pass a structured payload with title, color, fields, and timestamp rather than a single string. Keep the formatting logic in your notifier, not inline at call sites — the package is a transport, not a templating engine.
Queue the send. Incoming webhooks are synchronous HTTP calls. Never block a user request on Slack, and never call it from a hot code path. Dispatch a job or Notification that performs the send:
SendSlackAlert::dispatch($text)->onQueue('notifications');
Route by environment. Configure separate webhooks per environment (dev sends to #dev-noise, prod to #alerts) via distinct .env values. Many teams also prefix the username or icon with the environment name so a stray staging message never looks like a production page.
Integrate via the exception handler for alerts. A common day-one pattern is to report uncaught exceptions and failed queued jobs to Slack. Do it inside the handler's report()/render() or a dedicated job — not synchronously in report() — or a Slack outage becomes a cascading failure.
Test the seam, not the vendor. In tests, either bind a fake into the container or use Http::fake() (the package ultimately issues an HTTP call to a webhook URL) and assert on the outbound payload. This protects you against the next package bump.
Reuse config, not hardcoded strings. Keep channel names, usernames, and icon emojis in config/slack.php; keep the URL in .env. Per-message channel overrides should be an explicit, documented parameter in your notifier, not hidden defaults.
The webhook URL is a credential. Anyone with it can post to the channel. Keep it in .env/secret manager, never log it, and redact it in exception reporters. Rotate webhooks if a leaked URL appears in logs, tickets, or CI output.
Slack errors can be silent. A 200 with a body of ok means success; a 200 with invalid_payload or no_service does not throw. Check the package's return value (or wrap and inspect), because "it didn't crash" is not "it delivered."
Rate limits are real on notification-heavy apps. Slack throttles incoming webhooks (~1 message/second per webhook, with bursts). For batch alerts, aggregate into one message (multiple attachments or blocks) and add a queue throttle/middleware rather than fanning out dozens of calls.
Incoming webhooks are one-way. You cannot read messages, add reactions, thread replies, or edit later messages through this package. If you need those, that's the Slack Web API (slack/slack-php-api or laravel-notification-channels/slack) — don't try to bend this package to do it.
Config caching bites. After changing SLACK_WEBHOOK_URL or channel defaults on a cached deployment, run php artisan config:clear / config:cache. A stale webhook is the most common "it worked locally" bug with this package.
Auto-discovery / registration issues. If the Slack alias or binding isn't available after install/upgrade, run php artisan package:discover, then config:clear and cache:clear. On heavily customized apps, register the provider and alias manually in config/app.php.
Package maintenance is thin. Last release was 2020 (v2.6.0, Laravel 8 support). For a greenfield Laravel 10/11 app, either fork the package, or (usually easier) skip it and post to the webhook with Http::post() — it's a handful of lines — or use laravel-notification-channels/slack, which is actively maintained. Keep this package to legacy/Laravel 8 codebases where you want the facade ergonomics.
Never edit vendor/. If you need behavior changes (retries, custom HTTP client, signature headers), publish config, bind your own client, or fork — vendor edits vanish on the next composer update and make future upgrades fragile.
Rollback and debugging. If a post-upgrade Slack call misbehaves, compare composer show jeremykenedy/slack-laravel across environments, then Http::fake() a test that captures the outgoing payload. That quickly tells you whether it's a payload/formatting regression (your code) or a transport/HTTP change (the package or its dependencies).
Extension points: published config + container binding + your own wrapper class. That's the whole extension surface — lean on your wrapper for retries, payload enrichment, and swapping the transport later.
How can I help you explore Laravel packages today?