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 Client Laravel Package

mpociot/slack-client

A Laravel-friendly PHP client for the Slack Web API. Send messages, manage channels and users, and post rich attachments or blocks using a simple, fluent interface with config-driven tokens and a facade for quick integration.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check before you start. The package description advertises incoming webhooks and a Slack::send() API. The actual client in the repo is a thin wrapper around Slack's Web API plus an RTM (ReactPHP) listener — the call you'll use daily is Slack::sendMessage($channelId, $text, $attachments), authenticated with a bot token. Plan for that, not for a webhook URL. Last release was October 2018, so treat it as a legacy-maintenance dependency, not a greenfield pick.

Install and publish config

composer require mpociot/slack-client
php artisan vendor:publish --provider="Mpociot\Slack\SlackServiceProvider"

Laravel 5.5+ auto-discovers Mpociot\Slack\SlackServiceProvider and the Slack facade alias. On older versions, register them manually in config/app.php:

'providers' => [
    Mpociot\Slack\SlackServiceProvider::class,
],
'aliases' => [
    'Slack' => Mpociot\Slack\Facades\Slack::class,
],

Configure the token

config/slack.php exposes a single token key. Put a bot-user OAuth token in .env:

SLACK_TOKEN=xoxb-your-bot-token

Minimum scopes to get work done:

Scope Why you need it
chat:write Posting messages — the whole point
channels:read getChannels(), getChannelId()
users:read getUsers(), getUserId()
im:write DMing users directly
rtm:stream Only if you're maintaining the RTM loop

Where to look first

  1. config/slack.php — the only config surface you get.
  2. Mpociot\Slack\SlackServiceProvider — how the client is bound to the container; this is your override point in tests.
  3. Mpociot\Slack\Slack — the class you'll live in: sendMessage, getChannelId, getUserId, getChannels, getUsers.
  4. The RTM listener command — only if you're keeping a bot loop alive.

First use case — notify a channel from a job, exception handler, or artisan command

use Mpociot\Slack\Facades\Slack;

Slack::sendMessage('C01234567', 'Deploy finished :rocket:');

Resolve the channel ID once with Slack::getChannelId('deploys') (or copy it from the Slack UI) and store it in config. That single call covers the large majority of day-to-day Laravel needs: deploy notices, exception alerts, admin pings.

If you're inheriting a PHP 7.x / Laravel ≤8 app, verify the install lands cleanly first:

composer require mpociot/slack-client --dry-run
composer why-not mpociot/slack-client

The 2018 dependency graph (old Guzzle, old PSR, old event-loop libraries) is the most likely reason your first ten minutes turn into an afternoon.

Implementation Patterns

Resolve names to IDs once, then cache forever. The API speaks IDs; configs and humans speak names. The mapping is stable, so pay for the lookup exactly once.

$channelId = Cache::rememberForever("slack.channel.{$name}", fn () =>
    Slack::getChannelId($name)
);

$userId = Cache::rememberForever("slack.user.{$name}", fn () =>
    Slack::getUserId($name)
);

Wrap the facade behind your own interface — this is the single highest-value pattern. Never let Slack:: calls spread across controllers, jobs, and listeners.

// app/Services/SlackMessenger.php
interface SlackMessenger
{
    public function notify(string $channel, string $text, array $attachments = []): void;
}
// app/Services/SlackApiMessenger.php
class SlackApiMessenger implements SlackMessenger
{
    public function notify(string $channel, string $text, array $attachments = []): void
    {
        $channelId = Cache::rememberForever("slack.channel.{$channel}", fn () =>
            Slack::getChannelId($channel)
        );

        Slack::sendMessage($channelId, $text, $attachments);
    }
}

Bind it in a service provider. When you eventually swap to laravel/slack-notification-channel or a 20-line Guzzle client, exactly one class changes.

Send rich messages with attachments. This is how you get colored bars, titles with links, and structured fields.

app(SlackMessenger::class)->notify('ci', 'Build #123 failed', [[
    'fallback'   => 'Build failed on master',
    'color'      => 'danger',
    'title'      => 'CI failure',
    'title_link' => 'https://ci.example.com/builds/123',
    'fields'     => [
        ['title' => 'Branch', 'value' => 'master',  'short' => true],
        ['title' => 'Commit', 'value' => 'a1b2c3d', 'short' => true],
    ],
    'footer' => 'CI',
    'ts'     => time(),
]]);

Buttons and menus render, but they do nothing — interactivity requires Slack to call an HTTP endpoint you host, which this package does not provide.

Queue every outbound call. Each sendMessage is an HTTP round trip to Slack. Never block a web request or a synchronous job on it.

class SendSlackMessage implements ShouldQueue
{
    use InteractsWithQueue, Queueable;

    public int $tries = 3;
    public array $backoff = [5, 30, 120];

    public function __construct(
        public string $channel,
        public string $text,
        public array $attachments = []
    ) {}

    public function handle(SlackMessenger $slack): void
    {
        $slack->notify($this->channel, $this->text, $this->attachments);
    }
}

Hook into events, not domain code. Keep Slack out of business logic entirely.

class NotifySlackOnDeployFailure
{
    public function handle(DeployFailed $event): void
    {
        SendSlackMessage::dispatch(
            config('slack.alerts_channel'),
            ":x: Deploy failed: {$event->reason}"
        );
    }
}

Build small domain notifiers for recurring shapes. Deploy notices, error alerts, and review requests come up constantly — compose them once.

class DeployNotifier
{
    public function __construct(private SlackMessenger $slack) {}

    public function started(string $env, string $sha): void
    {
        $this->slack->notify(config('slack.deploy_channel'),
            ":rocket: Deploying `{$sha}` to *{$env}*");
    }

    public function failed(string $env, string $reason): void
    {
        $this->slack->notify(config('slack.deploy_channel'),
            ":x: Deploy to *{$env}* failed\n> {$reason}");
    }
}

Testing: bind a fake, don't hit the API. In feature tests, swap the interface binding in the container. If you'd rather mock the facade, assert on the arguments you send, not on Slack's response shape.

$this->instance(SlackMessenger::class, new class implements SlackMessenger {
    public array $sent = [];
    public function notify(string $c, string $t, array $a = []): void {
        $this->sent[] = compact('c', 't', 'a');
    }
});

RTM listener, if you're maintaining a legacy bot. It's a blocking ReactPHP loop:

Slack::on('message', function ($message) {
    if (str_contains($message['text'] ?? '', '!deploy')) {
        Slack::sendMessage($message['channel'], 'Deploying…');
    }
});

Slack::run(); // blocking — run under Supervisor, not `artisan serve`

Isolate the handler logic in its own class from day one. In-process state inside the loop is what makes the eventual Events API / Socket Mode migration painful.

Gotchas and Tips

The docs and the code disagree. The package description promises incoming webhooks and Slack::send(); the shipped client is a Web API wrapper requiring a bot token and channel IDs. Verify against Mpociot\Slack\Slack before you design around either. If you genuinely only need a webhook POST, you don't need this dependency at all.

RTM is a dead end for new apps. Slack no longer grants RTM access to newly created apps. Maintaining an existing bot is fine; building a new one is not possible regardless of library. Budget for porting to Socket Mode / slack-php/slack-bolt.

Silent WebSocket drop. The classic RTM failure mode: the connection half-opens, nothing throws, and the bot simply goes quiet. The package provides no application-level heartbeat. Add your own liveness signal (periodic log line, a ping task, or an aggressive Supervisor restart policy) or you'll learn about outages from a user pinging the bot.

Token type matters more than you'd think. This package assumes a legacy xoxb- bot-user token. Modern granular tokens with restricted scopes produce cryptic not_allowed_token_type or missing_scope errors — check scopes before blaming the library.

Pin the transitive graph. Six-year-old pinning means old Guzzle, old PSR versions, old WebSocket libraries. On Laravel 9+ / PHP 8, expect conflicts. Either fully pin via composer.lock, or — better — isolate the Slack integration in its own small service.

Don't confuse messages with slash-command responses. sendMessage posts messages. Slash commands and interactive buttons need an HTTP endpoint Slack calls. If the spec says "an Approve button that works," the answer is "Slack calls our webhook," not this client.

Handle 429 deliberately. Slack returns HTTP 429 with a Retry-After header. The package's built-in backoff behaviour is unverified in the current era — when you queue, catch 429s and honour the server-supplied delay rather than a fixed schedule.

Attachments cap out at ~100 per message and 20 fields each (≈4,000 chars per attachment text). Long log dumps get truncated or rejected; truncate yourself and link to the full output.

Escape user-supplied content. Slack renders <, >, &, and @-mentions specially. Unescaped user input can ping @channel. Strip <!channel> / <!here> and escape angle brackets unless you intend the ping.

Read the token at call time. Don't capture it in a constructor. If your workspace enforces token rotation, you'll want the fresh value from config('slack.token') (followed by php artisan config:clear) rather than a stale one baked into a long-lived object.

Debugging checklist when a message "doesn't send":

  1. SLACK_TOKEN in .env, and config cache cleared (php artisan config:clear).
  2. Token starts with xoxb- and includes chat:write.
  3. The bot is a member of the target channel — private channels require an explicit invite.
  4. The channel value is an ID starting with C, not #general.
  5. Check storage/logs/laravel.log for the underlying HTTP response — the package surfaces very little on failure, so log the exception yourself inside your SlackMessenger implementation.

The habit that pays off: the interface, always. One SlackMessenger interface, one implementation, zero Slack:: calls anywhere else. That single decision turns the eventual migration off a 2018-era, unmaintained, RTM-dead dependency into an afternoon's work instead of a sprint.

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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core