jolicode/slack-php-api
A complete, type-safe PHP client for the Slack API, generated from Slack's official OpenAPI specification. It covers every endpoint and model and works with any PSR-18 HTTP client and PSR-16 cache, so it drops easily into modern PHP projects.
Install the client and a PSR-18 compatible HTTP client:
composer require jolicode/slack-php-api symfony/http-client
Create a Slack app, add the bot scopes you need (chat:write, channels:read, users:read, etc.), install it to your workspace, and store the bot token outside source control.
The generated entry point is typically JoliCode\Slack\Api\Client; newer major versions may expose a JoliCode\Slack\Client convenience wrapper. Check the package README and the generated Api/ directory first — endpoint methods are named after Slack’s operations, e.g. chatPostMessage, conversationsList, usersInfo, authTest.
A minimal first call:
use JoliCode\Slack\Api\Client;
use Symfony\Component\HttpClient\HttplugClient;
$client = Client::createWithHttpClient(new HttplugClient());
$client->setToken($_ENV['SLACK_BOT_TOKEN']);
$auth = $client->authTest();
if (!$auth->getOk()) {
throw new RuntimeException('Slack auth failed: '.$auth->getError());
}
$response = $client->chatPostMessage([
'channel' => 'C0123456789', // channel ID, not #channel-name
'text' => 'Deployment finished ✅',
]);
if (!$response->getOk()) {
throw new RuntimeException('Slack error: '.$response->getError());
}
echo $response->getTs();
First practical use case: send a notification from a deploy script, queue worker, or monitoring job. Verify the token with authTest(), resolve channel IDs with conversationsList(), then post with chatPostMessage().
Register one client as a service. Instantiate the client once in your DI container and inject it into a small domain-specific wrapper rather than scattering generated API calls across the codebase.
final class SlackNotifier
{
public function __construct(private Client $client) {}
public function notifyDeploy(string $channelId, string $service, string $status): void
{
$response = $this->client->chatPostMessage([
'channel' => $channelId,
'text' => sprintf('%s deploy: %s', $service, $status),
'blocks' => [[
'type' => 'section',
'text' => [
'type' => 'mrkdwn',
'text' => sprintf("*%s* deploy: `%s`", $service, $status),
],
]],
]);
if (!$response->getOk()) {
throw new SlackException($response->getError() ?? 'unknown_error');
}
}
}
Treat endpoint arguments as Slack’s Web API parameters. Generated methods accept an associative array using Slack’s parameter names (channel, text, blocks, cursor, limit). IDE completion works for method names, but argument arrays are not strongly typed, so validate required parameters yourself.
Read responses through generated model getters. Responses are Jane-generated model objects: getOk(), getError(), getChannel(), getTs(), getMessage(), getResponseMetadata(), etc. Avoid relying on json_encode($response) for business logic; getters are more stable and type-checkable.
Handle Slack’s “HTTP 200 but failed” convention. Many Slack API errors return HTTP 200 with {"ok": false, "error": "..."}. Always check getOk() before assuming success. HTTP exceptions generally indicate transport, 5xx, or rate-limit problems.
Paginate explicitly. Slack list endpoints return a cursor, not an auto-paginated collection:
$cursor = null;
do {
$page = $client->conversationsList([
'limit' => 200,
'cursor' => $cursor,
]);
foreach ($page->getChannels() as $channel) {
// ...
}
$cursor = $page->getResponseMetadata()?->getNextCursor();
} while ($cursor);
Queue Slack calls instead of doing them inline in web requests. Slack rate limits are per-method and per-workspace. A queued job or async Symfony HttpClient call keeps your request latency and failure modes under control.
Configure HTTP timeouts and retries at the HTTP-client layer. Use Symfony HttpClient options (timeouts, retry middleware, proxy support) or a PSR-18 decorator. Slack returns 429 with Retry-After for rate limiting; honor it.
Use incoming webhooks for simple one-way notifications. Use this package when you need API responses, channel/user lookup, message updates, file handling, or richer Slack app behavior.
Test with a fake PSR-18 client. Since the generated client takes an HTTP client, you can assert the outgoing request without hitting Slack:
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;
use Symfony\Component\HttpClient\Psr18Client;
$http = new MockHttpClient([
new MockResponse(json_encode(['ok' => true, 'ts' => '1700000000.000100'])),
]);
$client = Client::createWithHttpClient(new Psr18Client($http));
$client->setToken('xoxb-test');
Keep signing-secret verification separate. This package is a Slack Web API client. It does not receive Events API payloads or verify Slack request signatures; handle that in your HTTP layer.
ok can be false on success-looking HTTP responses. Check getOk() on every response. Missing scopes, invalid channel IDs, and malformed blocks often appear as ok: false, not exceptions.chat:write posts to channels the bot is in; chat:write.public posts to public channels without joining. channels:read, groups:read, and users:read are separate. missing_scope responses include needed and provided scopes — log the full response during setup.#general may work in some contexts but IDs are stable. Resolve names via conversationsList and cache the mapping.invalid_blocks. If Slack rejects your blocks array, inspect the outgoing request. Some clients/endpoints expect blocks as a JSON-encoded string rather than a nested form field. A logging HTTP middleware is invaluable here.chatPostMessage calls can return 429. Implement backoff at the job level or via an HTTP retry decorator; never tight-loop retries.?bool, ?string, and nullable nested objects. Use null-safe operators and PHPStan/Psalm to catch assumptions.ts values are strings like "1700000000.000100". Do not cast them to floats for equality or ordering; compare as strings or with arbitrary-precision handling.Authorization headers and request bodies in logs, especially in exception handlers and HTTP middleware.authTest() makes a good startup/health check. It validates the token and returns team/user identity without side effects.How can I help you explore Laravel packages today?