When we launched Flare, we could notify you over errors occurring via Slack, Mail, SMS and Webhooks. When we take a look at our database, Slack seems to be the predominant way for teams to communicate.
In the meantime however, we got a lot of questions to add support for alternative notification platforms. Today, we're adding support for sending notifications via Discord and Microsoft Teams.
We took great care in formatting exception just right. If your teams uses Flare and Discord, this is how exceptions will look like in the Discord UI.

And here's how's it looks like when using Microsoft Teams.

In our documentation you can read how you can start using these new notification channels.
How these notification channels are implemented
Behind the scenes, Flare is a Laravel application. Out of the box, Laravel has easy to extend system for sending out notifications.
For Discord, we quickly implemented our own Discord notification channel and message.
This is the code of the DiscordChannel class.
namespace App\Domain\Notification\Services\Channels\Discord;
use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;
use Illuminate\Notifications\Notification;
class DiscordChannel
{
public function send($notifiable, Notification $notification): void
{
$discordMessage = $notification->toDiscord();
$discordWebhook = $notifiable->routeNotificationForDiscord();
(new Client())->post($discordWebhook, [
RequestOptions::JSON => $discordMessage->toArray(),
]);
}
}
And here's the code for the DiscordMessage class. You'll notice that it's a bit opinionated. For convenience, we've added an errorOccurrence method directly on the class that can format a given ErrorOccurrence model.
namespace App\Domain\Notification\Services\Channels\Discord;
use App\Domain\Error\Models\ErrorOccurrence;
use App\Http\App\Controllers\Projects\ShowProjectController;
use Carbon\Carbon;
class DiscordMessage
{
public const COLOR_SUCCESS = '0b6623';
public const COLOR_WARNING = 'fD6a02';
public const COLOR_ERROR = 'e32929';
protected string $title = '';
protected string $description = '';
protected array $fields = [];
protected ?string $timestamp = null;
protected ?string $footer = null;
protected ?string $color = null;
protected string $url = '';
public function title($title)
{
$this->title = $title;
return $this;
}
public function url($url)
{
$this->url = $url;
return $this;
}
public function description($descriptionLines): self
{
if (! is_array($descriptionLines)) {
$descriptionLines = [$descriptionLines];
}
$this->description .= implode(PHP_EOL, $descriptionLines);
return $this;
}
public function timestamp(Carbon $carbon): self
{
$this->timestamp = $carbon->toIso8601String();
return $this;
}
public function footer(string $footer): self
{
$this->footer = $footer;
return $this;
}
public function success(): self
{
$this->color = static::COLOR_SUCCESS;
return $this;
}
public function warning(): self
{
$this->color = static::COLOR_WARNING;
return $this;
}
public function error(): self
{
$this->color = static::COLOR_ERROR;
return $this;
}
public function fields(array $fields, bool $inline = true): self
{
foreach ($fields as $label => $value) {
$this->fields[] = [
'name' => $label,
'value' => $value,
'inline' => $inline,
];
}
return $this;
}
public function errorOccurrence(ErrorOccurrence $errorOccurrence, string $message): self
{
$projectUrl = action(ShowProjectController::class, $errorOccurrence->error->project->idSlug());
$stage = $errorOccurrence->stage
? "({$errorOccurrence->stage})"
: "";
$this->title("{$errorOccurrence->error->project->name} {$stage}");
$this->url($projectUrl);
$fileLocation = "{$errorOccurrence->error->file}:`{$errorOccurrence->error->line_number}`";
if ($firstFrame = $errorOccurrence->firstFrame()) {
$fileLocation = "{$firstFrame->file}:`{$firstFrame->lineNumber}`";
}
$properties = [
"URL: {$errorOccurrence->seen_at_url}",
$message,
'',
"[See details on Flare]({$errorOccurrence->reportUrl()})",
"**{$errorOccurrence->exception_message}**",
'',
"**File:** {$fileLocation}",
'',
];
if ($firstFrame) {
$codeSnippet = $firstFrame->shortenCodeSnippetWithCurrentLineMark();
$occurredIn = "`{$firstFrame->class}::{$firstFrame->method}`" . PHP_EOL;
$this->fields([
$occurredIn => "```{$errorOccurrence->error->language}\n{$codeSnippet}```",
], inline: false);
}
$this->fields([
'Stage' => $errorOccurrence->stage,
'Exception' => $errorOccurrence->exception_class,
'Occurrence count' => $errorOccurrence->error->occurrence_count,
'Affected users' => $errorOccurrence->error->affected_user_count,
]);
$this->description($properties);
$this->timestamp($errorOccurrence->received_at);
return $this;
}
public function toArray(): array
{
return [
'username' => 'Flare',
'avatar_url' => 'https://flareapp.io/images/flare-avatar.png',
'embeds' => [
[
'title' => $this->title,
'url' => $this->url,
'type' => 'rich',
'description' => $this->description,
'fields' => $this->fields,
'color' => hexdec($this->color),
'footer' => [
'text' => $this->footer ?? '',
],
'timestamp' => $this->timestamp,
],
],
];
}
}
With this in place, we can add a toDiscord method on our notification class.
namespace App\Domain\Error\Notifications\ErrorOccurred;
use App\Domain\Error\Models\ErrorOccurrence;
use App\Domain\Notification\Services\Channels\Discord\DiscordMessage;
class NewErrorOccurredNotification extends Notification
{
public function __construct(
public ErrorOccurrence $errorOccurrence
) {}
// other notifications channels omitted for brevity...
public function toDiscord()
{
return (new DiscordMessage())
->error()
->errorOccurrence(
$this->errorOccurrence,
'A new error occurred',
);
}
}
In closing
We're very happy with these new notification channels, and hope you'll like them as well. In the near future, you can expect more improvements around Flare's notification sytem.
If you want use to support your favourite notification channels, or have suggestions on how to make Flare better, let us know. We're listening!
Continue reading
Introducing the Flare CLI
Manage your errors, projects, and performance data straight from the terminal with the new Flare CLI. Install it via Composer and get full access to your Flare dashboard without opening a browser.
Alex
Let your AI coding agent fix errors and review performance
Give your AI coding agent direct access to your errors and performance data with a single command. No MCP server needed. Just install the skill and let your agent investigate, fix, and triage issues without leaving the terminal.
Alex
Subscribe to Backtrace, our quarterly Flare newsletter
No spam, just news & product updates