labrodev / laravel-slack
Stateless Slack incoming-webhook client for Laravel with Block Kit message transport and webhook-URL validation.
Requires
- php: ^8.4
- guzzlehttp/guzzle: ^7.8
- illuminate/http: ^13.0
- illuminate/support: ^13.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- driftingly/rector-laravel: ^2.5
- larastan/larastan: ^3.0
- laravel/pint: ^1.0
- orchestra/testbench: ^11.0
- pestphp/pest: ^4.0
README
Stateless Slack incoming-webhook client for Laravel with Block Kit message transport and webhook-URL validation. Slack incoming webhooks are self-contained bearer URLs — there is no OAuth flow, no bot token, and no app-level credential to configure. Each caller supplies its own webhook URL on every call, and the package stores nothing.
Installation
composer require labrodev/laravel-slack
Optionally publish the config file:
php artisan vendor:publish --tag=slack-config
SLACK_HTTP_TIMEOUT=10 SLACK_WEBHOOK_HOST=hooks.slack.com SLACK_WEBHOOK_PATH_PREFIX=/services/
SLACK_WEBHOOK_HOST and SLACK_WEBHOOK_PATH_PREFIX only need overriding if Slack ever changes its incoming-webhook domain or path shape — the defaults match Slack's current URLs.
Sending a message
SlackClient::send() posts Block Kit blocks and a fallback text to the incoming-webhook URL you provide:
use Labrodev\Slack\Services\SlackClient; use Labrodev\Slack\Exceptions\SlackRequestFailed; use Labrodev\Slack\Exceptions\SlackWebhookRevoked; $slackClient = new SlackClient(); $blocks = [ [ 'type' => 'section', 'text' => [ 'type' => 'mrkdwn', 'text' => 'A new incident was reported.', ], ], ]; try { $slackClient->send( webhookUrl: $webhookUrl, blocks: $blocks, fallbackText: 'A new incident was reported.', ); } catch (SlackWebhookRevoked) { // the webhook was deleted or revoked on Slack's side } catch (SlackRequestFailed) { // any other failed request }
Validating a webhook URL
Use SlackWebhookUrl::validate() to check a pasted webhook URL before storing it. It checks the scheme, the host against slack.webhook_host, and the path against slack.webhook_path_prefix, without overfitting to the exact segment count — and throws a specific exception for whichever part failed:
use Labrodev\Slack\Support\SlackWebhookUrl; use Labrodev\Slack\Exceptions\SlackWebhookUrlSchemeInvalid; use Labrodev\Slack\Exceptions\SlackWebhookUrlHostInvalid; use Labrodev\Slack\Exceptions\SlackWebhookUrlPathInvalid; try { new SlackWebhookUrl()->validate($request->input('webhook_url')); } catch (SlackWebhookUrlSchemeInvalid|SlackWebhookUrlHostInvalid|SlackWebhookUrlPathInvalid) { throw ValidationException::withMessages([ 'webhook_url' => 'That does not look like a Slack incoming-webhook URL.', ]); }
Detecting a revoked webhook
Catch SlackWebhookRevoked to mark a stored webhook credential as dead instead of retrying it indefinitely:
use Labrodev\Slack\Exceptions\SlackWebhookRevoked; try { $slackClient->send(webhookUrl: $channel->webhook_url, blocks: $blocks, fallbackText: $fallbackText); } catch (SlackWebhookRevoked) { $channel->update(['webhook_url' => null]); }
Exceptions
Labrodev\Slack\Exceptions\SlackWebhookRevoked— Slack returned404or410: the webhook was deleted or revoked.Labrodev\Slack\Exceptions\SlackRequestFailed— any other failed request.Labrodev\Slack\Exceptions\SlackWebhookUrlSchemeInvalid— thrown bySlackWebhookUrl::validate()when the scheme isn'thttps.Labrodev\Slack\Exceptions\SlackWebhookUrlHostInvalid— thrown bySlackWebhookUrl::validate()when the host doesn't matchslack.webhook_host.Labrodev\Slack\Exceptions\SlackWebhookUrlPathInvalid— thrown bySlackWebhookUrl::validate()when the path doesn't start withslack.webhook_path_prefix.
All carry fixed messages only. A Slack incoming-webhook URL is the bearer secret itself, so it is never interpolated into an exception message.
Testing
composer test # pest composer phpstan # larastan, level 7 composer pint # laravel preset composer check # all of the above
License
MIT. See LICENSE.md.