Push notifications from PHP
Your PHP app processes something important. You find out by checking logs or the admin panel.
Here’s how to make PHP tell you directly.
The simplest version: curl
No library needed:
<?php
function notify(string $title): void {
$key = getenv('TRIGGER_FYI_SECRET_KEY');
if (!$key) return;
$ch = curl_init("https://trigger.fyi/{$key}");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $title,
CURLOPT_HTTPHEADER => ['Content-Type: text/plain'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 3,
]);
curl_exec($ch);
curl_close($ch);
}
notify('Job complete');Errors are ignored — fire-and-forget. Your application doesn’t wait for or depend on the notification.
With metadata and levels
<?php
function fyi(string $title, array $options = []): void {
$key = getenv('TRIGGER_FYI_SECRET_KEY');
if (!$key) return;
$body = ['title' => $title];
if (isset($options['body'])) $body['body'] = $options['body'];
if (isset($options['level'])) $body['level'] = $options['level'];
$meta = array_diff_key($options, array_flip(['body', 'level']));
if ($meta) $body['meta'] = $meta;
$ch = curl_init("https://trigger.fyi/{$key}");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($body),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 3,
]);
curl_exec($ch);
curl_close($ch);
}
// Normal push
fyi('New signup', ['body' => $user['email']]);
// Feed only — no push
fyi('Cache warmed', ['level' => 'log', 'body' => '12,400 keys']);
// High urgency
fyi('Payment failed', ['level' => 'critical', 'body' => $user['email']]);
// With metadata
fyi('New signup', ['body' => $user['email'], 'plan' => $user['plan'], 'country' => $user['country']]);In Laravel
Add to app/Services/NotificationService.php:
<?php
namespace App\Services;
class Fyi
{
public static function send(string $title, array $options = []): void
{
$key = config('services.trigger_fyi.key');
if (!$key) return;
dispatch(function () use ($key, $title, $options) {
$payload = array_merge(['title' => $title], $options);
Http::timeout(3)->post("https://trigger.fyi/{$key}", $payload);
})->afterResponse();
}
public static function log(string $title, array $options = []): void
{
self::send($title, array_merge($options, ['level' => 'log']));
}
public static function critical(string $title, array $options = []): void
{
self::send($title, array_merge($options, ['level' => 'critical']));
}
}In config/services.php:
'trigger_fyi' => [
'key' => env('TRIGGER_FYI_SECRET_KEY'),
],Usage in controllers:
use App\Services\Fyi;
class SignupController extends Controller
{
public function store(Request $request)
{
$user = User::create($request->validated());
Fyi::send('New signup', ['body' => $user->email, 'plan' => $user->plan]);
return response()->json($user, 201);
}
}afterResponse() fires after the HTTP response is sent — the user never waits for the notification.
In a cron script
#!/usr/bin/env php
<?php
$start = microtime(true);
try {
$result = runBackup();
$elapsed = round(microtime(true) - $start);
notify("Backup complete", ['body' => "{$result['items']} items · {$elapsed}s"]);
} catch (Throwable $e) {
notify("Backup failed", ['level' => 'critical', 'body' => $e->getMessage()]);
exit(1);
}In a Stripe webhook handler
<?php
$payload = @file_get_contents('php://input');
$sig = $_SERVER['HTTP_STRIPE_SIGNATURE'] ?? '';
try {
$event = \Stripe\Webhook::constructEvent($payload, $sig, $_ENV['STRIPE_WEBHOOK_SECRET']);
} catch (\Exception $e) {
http_response_code(400);
exit();
}
http_response_code(200);
echo 'OK';
// After response — flush output buffer
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
}
if ($event->type === 'payment_intent.succeeded') {
$amount = number_format($event->data->object->amount / 100, 2);
$email = $event->data->object->receipt_email;
fyi("Payment: \${$amount}", ['body' => $email]);
}fastcgi_finish_request() flushes and closes the connection to the client while the PHP process continues. The notification fires after the client receives 200.
Setup
npx trigger.fyiGenerates a key. Add to your .env:
TRIGGER_FYI_SECRET_KEY=your_key_here
Or set it in your server environment, Laravel config, or deployment platform.
Related: Push notifications from Ruby · Push notifications from Node.js · Deploy notifications on your phone · What is trigger.fyi?