Setup
Open your application settings
Navigate to your application’s Issues settings in the Nightwatch dashboard.
Add a webhook
In the Webhooks section, configure your webhook:
- Name: A descriptive label for your reference (e.g., “Slack notifications”, “Internal API”)
- URL: An HTTPS endpoint that accepts POST requests
Only one webhook per application is allowed. To change the URL or name, edit the existing webhook configuration.
Every webhook request is an HTTP POST with a JSON body of this shape:
| Field | Type | Description |
|---|
event | string | Event type (e.g. issue.opened, issue.resolved) |
timestamp | string | ISO 8601 datetime when the event was sent (e.g. 2026-02-18T12:00:00.000000Z) |
payload | object | Event data (see below) plus metadata |
The payload object always includes:
| Field | Type | Description |
|---|
webhook_id | string | UUID of the webhook |
application_id | string | UUID of the application |
organization_id | string | UUID of the organization |
Plus the event-specific fields described for each event below.
Events
issue.opened
Sent when a new issue is created (first time the exception or performance problem is detected).
When it fires: A new issue is created for the application (e.g. new exception group or new slow route/job).
Payload (in addition to metadata):
| Field | Type | Description |
|---|
issue | object | The issue (see Issue object) |
environment | object | Environment where the issue was detected (see Environment object) |
issue.reopened
Sent when an issue that was previously resolved is seen again and reopened.
When it fires: The same exception or performance issue is detected again after the issue was closed.
Payload (in addition to metadata):
| Field | Type | Description |
|---|
issue | object | The issue (see Issue object) |
environment | object | Environment where the issue was detected (see Environment object) |
issue.ignored
Sent when an issue’s status is set to Ignored.
When it fires: A user (or automation) marks the issue as ignored.
Payload (in addition to metadata):
| Field | Type | Description |
|---|
issue | object | The issue (see Issue object) |
actor | object | User who performed the action, if any (see Actor object) |
issue.resolved
Sent when an issue’s status is set to Resolved.
When it fires: A user (or automation) marks the issue as resolved.
Payload (in addition to metadata):
| Field | Type | Description |
|---|
issue | object | The issue (see Issue object) |
actor | object | User who performed the action, if any (see Actor object) |
Shared payload objects
Issue object
| Field | Type | Description |
|---|
id | string | Issue UUID |
ref | int | Display number (e.g. issue #123) |
type | string | "exception" or "alert" |
title | string | Issue title |
status | string | "open", "resolved", or "ignored" |
priority | string | "none", "low", "medium", or "high" |
url | string | Link to the issue in the dashboard |
Environment object
| Field | Type | Description |
|---|
id | string | Environment UUID |
name | string | Environment name |
Actor object
Present when the action was performed by a user.
| Field | Type | Description |
|---|
id | string | User UUID |
name | string | User display name |
email | string | User email |
type | string | Always "user" |
Example request
{
"event": "issue.resolved",
"timestamp": "2026-02-18T12:00:00.000000Z",
"payload": {
"issue": {
"id": "9d4e2c1a-1234-5678-abcd-ef0123456789",
"ref": 42,
"type": "exception",
"title": "Illuminate\\Database\\QueryException: SQLSTATE[23502]...",
"status": "resolved",
"priority": "high",
"url": "https://nightwatch.laravel.com/us/applications/cf592d1a-5f91-4f3c-9c10-8c67013c4469/issues/42"
},
"actor": {
"id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"name": "Jane Doe",
"email": "jane@example.com",
"type": "user"
},
"webhook_id": "fa2d5c2d-3b8c-41ad-8cf6-52f80d446818",
"application_id": "cf592d1a-5f91-4f3c-9c10-8c67013c4469",
"organization_id": "9cfeeac6-d449-4924-852b-c3a3294db9c2"
}
}
Verifying signatures
Each webhook request includes a Nightwatch-Signature header that allows you to verify the request came from Nightwatch and has not been tampered with.
Get your signing secret
You can find and copy the signing secret at any time in your application’s Issues settings > Webhooks > Edit.
Compute the expected signature
Using the raw request body (the exact JSON string received) and your signing secret, compute an HMAC-SHA256 signature:
- Algorithm: HMAC-SHA256
- Message: Raw request body
- Key: Your webhook signing secret
Compare signatures
Compare the computed signature with the Nightwatch-Signature header using a constant-time comparison to prevent timing attacks. If they match, the request is authentic.
Store your webhook signing secret in your application’s configuration file or environment variables for security.
Laravel example:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class NightwatchWebhookController extends Controller
{
public function handle(Request $request): Response
{
$payload = $request->getContent();
$signature = $request->header('Nightwatch-Signature');
$secret = config('services.nightwatch.webhook_secret');
$expectedSignature = hash_hmac('sha256', $payload, $secret);
if (! hash_equals($expectedSignature, $signature)) {
abort(401, 'Invalid signature');
}
// Process the verified webhook payload
$data = json_decode($payload, true);
// Handle the webhook event based on $data['event']
match ($data['event']) {
'issue.opened' => $this->handleIssueOpened($data['payload']),
'issue.resolved' => $this->handleIssueResolved($data['payload']),
'issue.reopened' => $this->handleIssueReopened($data['payload']),
'issue.ignored' => $this->handleIssueIgnored($data['payload']),
default => logger()->info('Unknown webhook event', ['event' => $data['event']]),
};
return response()->json(['status' => 'success']);
}
private function handleIssueOpened(array $payload): void
{
// Handle new issue notification
}
private function handleIssueResolved(array $payload): void
{
// Handle issue resolution notification
}
private function handleIssueReopened(array $payload): void
{
// Handle issue reopening notification
}
private function handleIssueIgnored(array $payload): void
{
// Handle issue ignored notification
}
}
Configuration example (config/services.php):
return [
// Other service configurations...
'nightwatch' => [
'webhook_secret' => env('NIGHTWATCH_WEBHOOK_SECRET'),
],
];