Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Slack Laravel Package

alek13/slack

A Laravel package for Slack integration that makes it easy to send messages and notifications to Slack channels via incoming webhooks or the Slack API, with rich message formatting, configurable settings, and a simple, expressive PHP interface.

Deep Wiki
Context7

Getting Started

Install and wire it up. The package is a webhook client, not a full Slack SDK — one Composer line and a URL is genuinely all it takes.

composer require alek13/slack
// .env
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T000/B000/xxxx
SLACK_CHANNEL=#alerts

In Laravel, publish the config and check what you got:

php artisan vendor:publish --provider="Alek13\Slack\Laravel\ServiceProvider"

That drops config/slack.php with the settings you'll actually touch: webhook_url, channel, username, icon, link_names, unfurl_links, unfurl_media, allow_markdown, markdown_in_attachments. If the package isn't auto-discovered on your version, register Alek13\Slack\Laravel\ServiceProvider in bootstrap/providers.php (or config/app.php) and alias the facade as Slack.

First use case: a deploy notification from a console command. This is the highest-value, lowest-risk entry point — no user request is waiting on Slack, and if the webhook is down you just lose a nice-to-have message.

use Alek13\Slack\Laravel\Facade as Slack;

Slack::to('#deploys')
    ->from('Deploybot')
    ->attach([
        'fallback' => 'Deploy finished: [email protected]',
        'color'    => 'good',
        'title'    => '[email protected] deployed to production',
        'text'     => "Duration: 2m 14s\nBy: {$user}",
    ])
    ->send('Deploy complete');

Where to look first. Read config/slack.php end to end — it's the whole feature surface. Then read the three classes you'll actually use: Client (transport + defaults), Message (channel/username/icon/markdown/attachments), and Attachment / AttachmentField (the structured payload). Anything not in those three files is not supported, and knowing that up front saves you from hunting for features that don't exist.

Implementation Patterns

Wrap it in one service, don't sprinkle Slack:: calls everywhere. Webhook payloads are notoriously fiddly to get right, and you want exactly one place to change when you add retries, logging, or move to the Web API later.

namespace App\Support;

use Alek13\Slack\Client;
use Alek13\Slack\Attachment;
use Illuminate\Support\Facades\Log;

class SlackNotifier
{
    public function __construct(private Client $client) {}

    public function alert(string $title, string $text, string $color = 'warning'): void
    {
        try {
            $this->client
                ->to(config('slack.alerts_channel', '#alerts'))
                ->attach([['fallback' => $title, 'color' => $color, 'title' => $title, 'text' => $text]])
                ->send($title);
        } catch (\Throwable $e) {
            // never let alerting take down the thing it's alerting about
            Log::error('Slack delivery failed', ['error' => $e->getMessage(), 'title' => $title]);
        }
    }
}

Bind the Client in a service provider from config (new Client(config('slack.webhook_url'), config('slack'))) so your wrapper is injectable and swappable.

Use named clients for multiple workspaces or channels. Don't mutate the singleton's defaults at runtime — construct separate clients from a config/slack.php array keyed by destination. This also makes per-destination testing trivial.

'webhooks' => [
    'alerts'  => ['url' => env('SLACK_WEBHOOK_ALERTS'),  'channel' => '#alerts'],
    'deploys' => ['url' => env('SLACK_WEBHOOK_DEPLOYS'), 'channel' => '#deploys'],
],

Attachments are your layout language. Everything visually interesting — colored bars, titles, author blocks, footers, timestamps, side-by-side fields — lives in Attachment. Reach for AttachmentField for anything you'd otherwise fake with padding:

$attachment = (new Attachment())
    ->setColor('danger')
    ->setTitle('Queue backlog exceeded')
    ->setText('jobs:default has `12,431` pending jobs.')
    ->addField((new AttachmentField())->setTitle('Threshold')->setValue('10,000')->setShort(true))
    ->addField((new AttachmentField())->setTitle('Host')->setValue(gethostname())->setShort(true))
    ->setFooter('monitoring')->setTimestamp(time());

Slack::to('#alerts')->attach($attachment)->send('Queue backlog');

Never send inline from a request cycle. A slow or hanging Slack endpoint becomes a slow endpoint for your users. Two good options: dispatch a queued job (SendSlackMessage implements ShouldQueue, $tries = 3, $backoff = [10, 60]), or push to dispatch()->afterResponse() for fire-and-forget notifications. Pair every queued job with failed() handling that logs — a job that silently dies is worse than a log line.

Route through a custom Laravel notification channel if you already use Notifications. There's no first-party channel here, so write a small SlackWebhookChannel that calls your SlackNotifier. That gives you via(), per-user preferences, and Notification::route() for ad-hoc recipients without inventing your own routing layer.

Digest, don't stream. Because webhooks are rate-limited per channel, batch noisy events. Collect exceptions in a scheduled command and post one grouped attachment (one field per error, capped at 10) rather than firing a message per exception and getting throttled into silence.

Gotchas and Tips

Incoming webhooks only — no Web API. This is the single biggest constraint. No thread replies, no chat.update to edit a message, no reactions, no file uploads, no ephemeral messages, no modals, no slash-command responses, no reading channel history. If your roadmap includes interactive buttons or threaded bot conversations, budget for a real Slack SDK instead; don't try to bolt it onto this.

No Block Kit. Slack's modern message format (blocks) has superseded attachments. This package speaks the JSON POST shape centered on text + attachments, which Slack still renders but no longer recommends. Buttons and selects won't work. Verify current rendering with a real post before you build a layout around it — the format is on a long deprecation runway, not a stable foundation.

Http::fake() will not intercept it. The client uses Guzzle directly, so Laravel's HTTP fake is useless here. In tests, either swap the container binding ($this->app->instance(Client::class, $fake), or the facade's shouldReceive('send') if the binding is a singleton — check the service provider for the actual key), or point webhook_url at a local listener. Never let CI hit a real webhook.

Config caching bites you here. config:cache in deploy means env() calls inside config/slack.php get frozen at cache time. Rotate a webhook URL and forget to re-cache, and you're posting to a dead endpoint with zero errors visible until you inspect the exception you're swallowing.

Channel override is not guaranteed. .to('#channel') (or @user for a DM) relies on the webhook's own configuration permitting overrides. Webhooks created by some Slack apps are locked to a single channel, and the override silently sends to the locked destination. If messages "go to the wrong place," that's the cause — not the package.

Rate limits are per channel and unforgiving. Roughly one message per second per channel, plus a broader per-webhook ceiling. A retry storm during an incident will get you throttled exactly when you need alerts most. Add a debounce/coalesce key before sending, and prefer one aggregated alert over N individual ones.

Set a timeout. Default Guzzle timeouts are long enough to be a real problem in a synchronous path. If the constructor accepts an options array, pass a short timeout and the relevant curl_options; otherwise configure it where you build the client. Two seconds is plenty for a webhook POST.

Escape user-supplied text. Slack treats &, <, > specially, and < starts link/mention parsing — so an exception message containing <html> or <!channel> will mangle formatting or spam the channel. Manually replace & → &amp;, < → &lt;, > → &gt; in anything dynamic. Truncate too: ~4000 chars per text field, 20 attachments max, ~100 fields per attachment, ~150 chars per field title.

Markdown isn't Markdown. It's Slack mrkdwn: *bold* (single asterisks), _italic_, `code`, > blockquote. **bold** renders literally. allow_markdown and markdown_in_attachments in the config toggle escaping behavior, and getting them wrong is a common source of "why is my formatting showing as raw text." Test with link_names off first, then enable.

Pin the version. This is a community fork of a long-lived package (BSD-2), and "last release" dates on low-activity repos can be misleading. Lock a specific constraint in composer.json, keep the wrapper class as your abstraction seam, and skim the changelog when bumping — a fork's minor bump can change default markdown or escaping behavior. The wrapper is your insurance: if the package stalls, swapping in a direct Guzzle POST or a maintained SDK should touch one file, not eighty.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo