> For the complete documentation index, see [llms.txt](https://wiki.crosswatch.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wiki.crosswatch.app/crosswatch/settings/scheduling/scheduler-webhooks.md).

# Scheduler Webhooks

Configure notifications for scheduled syncs and system alerts.

Scheduler Webhooks let CW notify an external service when a scheduled sync starts, succeeds or fails. They can also warn you when the Watcher stops or a provider sign-in is lost. Typical uses include:

1. Getting push notifications on your phone through Pushover or ntfy
2. Monitoring scheduled syncs with Healthchecks.io
3. Sending scheduled sync notifications through Notifiarr
4. Forwarding run information to an automation platform
5. Sending structured JSON to a custom monitoring service

Webhook delivery is best effort. A failed or unavailable webhook does not fail the sync itself.

{% tabs %}
{% tab title="End users" %}

#### Where to find it

1. Open `Settings`
2. Open `Scheduling`
3. Open the `Webhooks` tab

The tab sits next to `Standard` and `Advanced`. Scheduler Webhooks work with both schedule types, and the alerts work even when no schedule is enabled.

#### The Webhooks tab

The tab is built as four steps with a status bar on top.

**Saved setup**

The bar at the top shows what is saved right now, not what is on screen.

| State         | Meaning                                           |
| ------------- | ------------------------------------------------- |
| `Active`      | A service is set up and webhooks are enabled      |
| `Off`         | A service is set up, but `Enable webhooks` is off |
| `Needs setup` | No usable destination has been saved yet          |
| `Unsaved`     | The form differs from what is saved               |

**Step 1: Choose where to send**

Pick one service. Only that service is used, and only its settings are shown.

| Service     | Use it for                                     |
| ----------- | ---------------------------------------------- |
| `Pushover`  | Push notifications on your phone               |
| `ntfy`      | Push notifications through an ntfy topic       |
| `Notifiarr` | Discord messages through Notifiarr Passthrough |
| `CW JSON`   | Your own endpoint or a Healthchecks ping URL   |

The saved service carries a `Saved` badge. Settings of the other services are kept when you switch.

**Step 2: Connect the service**

Fill in the fields of the selected service. Each field has a short hint underneath.

**Step 3: Choose what to send**

Scheduled sync results are always sent. The alerts below are optional.

| Option                  | What it does                                                                       |
| ----------------------- | ---------------------------------------------------------------------------------- |
| `Scheduled sync runs`   | Start, success and failure of every scheduled sync. Always on                      |
| `Unresolved items`      | Also reports a run as failed when it ends with unresolved items or provider errors |
| `Watcher stops`         | Alerts when an enabled Watcher route is not running, and again when it is back     |
| `Provider sign-in lost` | Alerts when a provider you use needs a new sign-in, and again when it works        |

**Step 4: Test it**

`Send test notification` sends one message with the saved setup.

1. Save your changes first. The button refuses to run while there are unsaved changes
2. The result names the service, for example `Test notification delivered to Pushover`
3. The test also works while `Enable webhooks` is off, so you can check a setup before turning it on

#### Basic setup

1. Open the `Webhooks` tab
2. Select a service in step 1
3. Fill in its connection details in step 2
4. Turn on the alerts you want in step 3
5. Turn on `Enable webhooks`
6. Save the Scheduler configuration
7. Click `Send test notification`

CW accepts only complete `http://` or `https://` URLs.

#### Pushover setup

Pushover needs two keys. Both are 30 characters long.

1. Open [pushover.net/apps/build](https://pushover.net/apps/build) and create an application, for example `CrossWatch`
2. Copy its API token into `Application token`
3. Copy your user key from the Pushover dashboard into `User key`. A group key works too
4. Enable Scheduler Webhooks
5. Save the Scheduler configuration
6. Click `Send test notification`

There is no URL field for Pushover. CW always sends to the Pushover message API.

A test with only the user key filled in sends nothing. Both keys are required.

#### ntfy setup

1. Select `ntfy`
2. Paste the full topic URL into `ntfy topic URL`
3. Enable Scheduler Webhooks
4. Save the Scheduler configuration
5. Click `Send test notification`

Example:

```
https://ntfy.sh/your-topic
```

CW publishes a plain text message with a title and a priority.

Things to know:

1. The ntfy server must be reachable on a public address. A self hosted ntfy server on a private network address is blocked. See Security
2. CW does not send an access token header. A protected topic needs the `auth` query parameter in the topic URL. See the ntfy documentation for its format
3. Anyone who knows a public topic name can read it. Pick a topic name that is hard to guess

#### Notifiarr setup

Notifiarr requires its own Passthrough payload format.

Configure CW as follows:

1. Select `Notifiarr`
2. Paste the Notifiarr Passthrough URL into `Notifiarr passthrough URL`
3. Optionally enter a numeric Discord channel ID
4. Enable Scheduler Webhooks
5. Save the Scheduler configuration

Example endpoint:

```
https://notifiarr.com/api/v1/notification/passthrough/YOUR_API_KEY
```

All notifications are sent to this same URL.

CW automatically generates the Notifiarr notification and Discord payload. It includes the run type, sync pair, workflow information, duration, exit code and available summary counters.

The channel ID is optional. Enter only the numeric Discord channel ID, without spaces or other characters.

Treat the Passthrough URL as a secret because it contains the Notifiarr API key.

#### Healthchecks.io setup

Healthchecks.io expects a base ping URL and supports `/start` and `/fail` suffixes.

Configure CW as follows:

1. Create or open a check in Healthchecks.io
2. Copy its Ping URL
3. Select `CW JSON`
4. Open `Use a Healthchecks ping URL or a separate URL per event`
5. Paste the Ping URL into `Compatible base URL`
6. Leave `All events URL` empty
7. Leave the three callback URL fields empty
8. Enable Scheduler Webhooks
9. Save the Scheduler configuration

Example:

```
Compatible base URL:
https://hc-ping.com/12345678-1234-1234-1234-123456789012
```

CW sends:

```
Start:
https://hc-ping.com/12345678-1234-1234-1234-123456789012/start

Success:
https://hc-ping.com/12345678-1234-1234-1234-123456789012

Failure:
https://hc-ping.com/12345678-1234-1234-1234-123456789012/fail
```

Query parameters are preserved when CW adds `/start` or `/fail`.

Example:

```
Base:
https://monitor.example/ping/crosswatch?source=crosswatch

Start:
https://monitor.example/ping/crosswatch/start?source=crosswatch

Failure:
https://monitor.example/ping/crosswatch/fail?source=crosswatch
```

CW does not expand variables inside URLs and does not automatically add the CW run ID as a query parameter.

Treat the complete Ping URL as a secret. Anyone with the URL can send events to the check.

With only a `Compatible base URL` configured:

1. `Send test notification` sends a success ping to the check
2. Watcher and provider sign-in alerts cannot be delivered. They need `All events URL`

#### Supported scheduled runs

Scheduler Webhooks are sent for these run types:

1. Standard scheduled syncs
2. Advanced timed sync pair jobs
3. Steps inside recurring workflows

Scheduler Webhooks are not sent for:

1. Syncs started from the normal Sync button
2. Scheduler event triggers
3. Scheduled captures
4. Scheduled backups

The Watcher and provider sign-in alerts are separate from scheduled runs. They are sent whenever their state changes, with or without a schedule.

#### Available events

**Start**

Sent when a scheduled sync has been accepted and begins execution.

The JSON event is:

```json
{
  "event": "start",
  "status": "started"
}
```

**Success**

Sent when the sync completes with exit code `0`.

The JSON event is:

```json
{
  "event": "success",
  "status": "ok",
  "exit_code": 0
}
```

**Failure**

Sent when the sync completes with a nonzero exit code, does not produce a valid exit code, or the Scheduler cannot start an expected sync.

The JSON event is:

```json
{
  "event": "failure",
  "status": "failed",
  "exit_code": 1
}
```

With `Unresolved items` turned on, a run that finishes with unresolved items or provider errors is also sent as a failure. The run itself still counts as successful in CW.

**Watcher stopped and Watcher recovered**

`watcher_stopped` is sent when an enabled Watcher route is not running. `watcher_recovered` is sent when it runs again.

This includes:

1. A Watcher that crashed
2. A route that failed to start
3. A Watcher that was stopped by hand
4. A Watcher that never started after a restart

No alert is sent when the route is disabled or the Watcher source is switched off.

**Provider sign-in lost and recovered**

`provider_auth_lost` is sent when a provider profile needs a new sign-in. `provider_auth_recovered` is sent when it works again.

Only provider profiles that are in use are checked:

1. Source and target of enabled sync pairs
2. Source and destination of enabled Watcher routes
3. Media server and destinations of active scrobble webhooks

A provider that is only unreachable does not trigger an alert. Network errors, timeouts and service outages are not treated as a lost sign-in.

**How the alerts behave**

1. Each change is sent once, not on every check
2. A problem must be seen on two checks in a row before an alert is sent
3. Several routes or providers that change at the same time are combined into one message
4. The Watcher is checked every minute. Provider sign-ins are checked every five minutes
5. The first check runs one minute after CW starts
6. After a restart of CW, a problem that still exists is reported once more

A revoked sign-in is noticed when CW next talks to that provider. On a setup with no running Watcher and no upcoming sync, that can take until the next sync.

#### Notification priority

Pushover and ntfy messages use a priority that depends on the event.

| Event                                     | Pushover           | ntfy        |
| ----------------------------------------- | ------------------ | ----------- |
| Start                                     | `-1` low, no sound | `2` low     |
| Success, recovered and test               | `0` normal         | `3` default |
| Failure, Watcher stopped and sign-in lost | `1` high           | `4` high    |

A Pushover or ntfy message looks like this:

```
CrossWatch scheduled sync failed

Scheduled sync failed. Check the CrossWatch sync logs for details.
Run: 7ab2bb3e1b7a4d24
Mode: standard
Pair: plex-trakt
Exit: 1
Errors: 2
```

#### Which URL field should I use

This applies to the `CW JSON` service.

**All events URL**

Use this when one webhook endpoint receives all events and uses the JSON `event` or `status` field to determine what happened.

Example:

```
https://hooks.example.com/crosswatch
```

CW sends start, success and failure events to the same URL. Watcher and provider sign-in alerts are also sent here.

Use this for:

1. Custom webhook receivers
2. Automation platforms with one incoming webhook
3. Logging or monitoring APIs that process JSON payloads
4. Services where the event type is determined from the request body

**Compatible base URL**

Use this for Healthchecks.io and other monitoring services that use the same URL convention.

CW derives the event URLs automatically:

```
Start:   <base URL>/start
Success: <base URL>
Failure: <base URL>/fail
```

Do not paste this URL into `All events URL`. When `All events URL` is configured, all events are sent to exactly that URL and the compatible base URL is ignored.

**Start callback URL**

Use this when the start event must be sent to a separate URL.

Example:

```
https://hooks.example.com/crosswatch/start
```

**Success callback URL**

Use this when successful runs must be sent to a separate URL.

Example:

```
https://hooks.example.com/crosswatch/success
```

**Failure callback URL**

Use this when failed runs must be sent to a separate URL.

Example:

```
https://hooks.example.com/crosswatch/failure
```

The event specific callback URLs override the other URL options for their event.

`Compatible base URL` and the three callback URLs are found under `Use a Healthchecks ping URL or a separate URL per event`. The section opens by itself when one of them is filled in.

#### URL priority

For the `CW JSON` service, CW selects a URL for start, success and failure in this order:

1. The callback URL for the specific event
2. All events URL
3. Compatible base URL

Example:

```
All events URL:        https://hooks.example.com/crosswatch
Failure callback URL:  https://alerts.example.com/crosswatch-failed
```

In this configuration:

```
Start goes to:   https://hooks.example.com/crosswatch
Success goes to: https://hooks.example.com/crosswatch
Failure goes to: https://alerts.example.com/crosswatch-failed
```

Watcher and provider sign-in alerts only use `All events URL`. The UI prevents saving an enabled configuration with those alerts turned on and no `All events URL`.

#### Timeout

The timeout controls how long CW waits for the webhook receiver.

```
Default: 10 seconds
Minimum: 1 second
Maximum: 60 seconds
```

A timeout only affects the webhook request. It does not change the sync result.
{% endtab %}

{% tab title="Power users" %}

#### Request behavior

CW sends every notification as an HTTP `POST` with the user agent `CW scheduler webhook`. The body depends on the service.

| Service     | Destination                                | Body                                                        |
| ----------- | ------------------------------------------ | ----------------------------------------------------------- |
| `CW JSON`   | Your URL                                   | `application/json`                                          |
| `Notifiarr` | Your Passthrough URL                       | `application/json`                                          |
| `Pushover`  | `https://api.pushover.net/1/messages.json` | Form fields `token`, `user`, `title`, `message`, `priority` |
| `ntfy`      | Your topic URL                             | Plain text, with `Title` and `Priority` headers             |

Each event is sent once.

CW does not currently provide:

1. Automatic retries
2. Configurable HTTP headers
3. Header based authentication
4. Payload templates
5. URL variable substitution
6. Webhook signing
7. Separate timeout values per event

A final HTTP `4xx` or `5xx` response is treated as a failed delivery.

Redirects are not followed. CW does not repeat the request at the new location, so use the final URL of the receiver.

A Pushover delivery only counts as accepted when Pushover answers HTTP `200` with `status` equal to `1`. Anything else is logged as rejected.

Message limits are applied before sending:

```
Pushover title:   250 characters
Pushover message: 1024 characters
ntfy message:     4096 bytes
```

Webhook delivery remains independent from the sync result.

#### CW JSON payload

A completed workflow step can produce a payload similar to this:

```json
{
  "event": "success",
  "status": "ok",
  "run_id": "7ab2bb3e1b7a4d24",
  "scheduler_mode": "advanced_workflow",
  "workflow_id": "nightly-sync",
  "workflow_step_id": "plex-to-trakt",
  "pair_id": "plex-trakt",
  "started_at": "2026-08-03T18:00:00Z",
  "finished_at": "2026-08-03T18:01:14Z",
  "duration_sec": 74.2,
  "exit_code": 0,
  "summary": {
    "added": 12,
    "removed": 1,
    "updated": 3,
    "unresolved": 2
  }
}
```

Empty values are omitted.

The possible top level fields are:

```
event
status
run_id
scheduler_mode
job_id
workflow_id
workflow_step_id
pair_id
started_at
finished_at
duration_sec
exit_code
summary
notices
```

The `summary` object can contain:

```
added
removed
updated
unresolved
errors
blocked
skipped
```

Zero value counters are normally omitted.

`notices` holds up to ten notices from the sync run and is omitted when there are none.

#### Watcher and provider alert payload

A Watcher alert in the `CW JSON` format:

```json
{
  "event": "watcher_stopped",
  "status": "failed",
  "title": "CrossWatch watcher stopped",
  "at": "2026-10-13T18:00:00Z",
  "items": [
    {
      "label": "Route R1",
      "detail": "Plex to Trakt",
      "route_id": "R1",
      "provider": "plex",
      "provider_instance": "default",
      "sink": "trakt",
      "sink_instance": "default"
    }
  ]
}
```

A provider sign-in alert:

```json
{
  "event": "provider_auth_lost",
  "status": "failed",
  "title": "CrossWatch provider sign-in lost",
  "at": "2026-10-13T18:05:00Z",
  "items": [
    {
      "label": "Trakt P02",
      "detail": "Trakt: unauthorized (token expired/revoked)",
      "provider": "trakt",
      "instance": "P02",
      "used_by": ["Sync pair plex-trakt", "Watcher route R1"]
    }
  ]
}
```

Possible values:

```
event: watcher_stopped
status: failed

event: watcher_recovered
status: ok

event: provider_auth_lost
status: failed

event: provider_auth_recovered
status: ok
```

`items` lists every route or provider profile that changed in that check.

#### Test payload

`Send test notification` sends this in the `CW JSON` format:

```json
{
  "event": "test",
  "status": "ok",
  "title": "CrossWatch test notification",
  "message": "Scheduler webhooks are set up correctly."
}
```

For `CW JSON`, the test goes to `All events URL`. Without it, the success destination is used.

#### Scheduler context values

The webhook service accepts these scheduler modes for start, success and failure:

```
standard
advanced
advanced_workflow
```

Other Scheduler modes do not generate these events.

**Standard schedule**

```json
{
  "source": "scheduler",
  "scheduler_mode": "standard"
}
```

**Advanced timed sync pair**

```json
{
  "source": "scheduler",
  "scheduler_mode": "advanced",
  "job_id": "job-id",
  "pair_id": "pair-id"
}
```

**Recurring workflow step**

```json
{
  "source": "scheduler",
  "scheduler_mode": "advanced_workflow",
  "workflow_id": "workflow-id",
  "workflow_step_id": "step-id",
  "pair_id": "pair-id"
}
```

#### Configuration structure

Scheduler Webhooks are stored inside the `scheduling` configuration.

```json
{
  "scheduling": {
    "webhooks": {
      "enabled": false,
      "url": "",
      "base_url": "",
      "start_url": "",
      "success_url": "",
      "failure_url": "",
      "payload_format": "crosswatch",
      "notifiarr_channel_id": "",
      "pushover_token": "",
      "pushover_user": "",
      "timeout_seconds": 10,
      "alert_on_unresolved": false,
      "alert_on_watcher": false,
      "alert_on_provider_auth": false
    }
  }
}
```

**Configuration fields**

`enabled`

Enables or disables Scheduler Webhook delivery. Alerts are also off when this is off.

`url`

The common URL for all events.

With `payload_format` set to `notifiarr`, this is the required Notifiarr Passthrough URL.

With `payload_format` set to `ntfy`, this is the required ntfy topic URL.

`base_url`

A Healthchecks.io compatible base URL.

CW adds `/start` and `/fail` when required.

`start_url`

Optional explicit destination for start events.

`success_url`

Optional explicit destination for successful completion events.

`failure_url`

Optional explicit destination for failed completion events.

`payload_format`

Supported values:

```
crosswatch
notifiarr
pushover
ntfy
```

The legacy value `notifiarr_passthrough` is normalized to `notifiarr`.

`notifiarr_channel_id`

Optional numeric Discord channel ID added to the Notifiarr payload.

`pushover_token`

The Pushover application token. Must be 30 letters or digits. Any other value is cleared.

`pushover_user`

The Pushover user key or group key. Must be 30 letters or digits. Any other value is cleared.

`timeout_seconds`

Webhook request timeout between `1` and `60` seconds.

Values below or above this range are limited to the supported range.

`alert_on_unresolved`

Sends the failure event when a run finishes with unresolved items or provider errors.

`alert_on_watcher`

Enables the `watcher_stopped` and `watcher_recovered` alerts.

`alert_on_provider_auth`

Enables the `provider_auth_lost` and `provider_auth_recovered` alerts.

#### Pushover configuration example

```json
{
  "scheduling": {
    "webhooks": {
      "enabled": true,
      "payload_format": "pushover",
      "pushover_token": "YOUR_30_CHARACTER_APPLICATION_TOKEN",
      "pushover_user": "YOUR_30_CHARACTER_USER_KEY",
      "timeout_seconds": 10,
      "alert_on_watcher": true,
      "alert_on_provider_auth": true
    }
  }
}
```

#### ntfy configuration example

```json
{
  "scheduling": {
    "webhooks": {
      "enabled": true,
      "url": "https://ntfy.sh/your-topic",
      "payload_format": "ntfy",
      "timeout_seconds": 10,
      "alert_on_watcher": true
    }
  }
}
```

#### Healthchecks.io configuration example

```json
{
  "scheduling": {
    "webhooks": {
      "enabled": true,
      "url": "",
      "base_url": "https://hc-ping.com/YOUR_CHECK_UUID",
      "start_url": "",
      "success_url": "",
      "failure_url": "",
      "payload_format": "crosswatch",
      "notifiarr_channel_id": "",
      "timeout_seconds": 10
    }
  }
}
```

#### Generic single endpoint configuration example

```json
{
  "scheduling": {
    "webhooks": {
      "enabled": true,
      "url": "https://hooks.example.com/crosswatch",
      "base_url": "",
      "start_url": "",
      "success_url": "",
      "failure_url": "",
      "payload_format": "crosswatch",
      "notifiarr_channel_id": "",
      "timeout_seconds": 10
    }
  }
}
```

#### Separate callback configuration example

```json
{
  "scheduling": {
    "webhooks": {
      "enabled": true,
      "url": "",
      "base_url": "",
      "start_url": "https://hooks.example.com/crosswatch/start",
      "success_url": "https://hooks.example.com/crosswatch/success",
      "failure_url": "https://hooks.example.com/crosswatch/failure",
      "payload_format": "crosswatch",
      "notifiarr_channel_id": "",
      "timeout_seconds": 10
    }
  }
}
```

#### Notifiarr configuration example

```json
{
  "scheduling": {
    "webhooks": {
      "enabled": true,
      "url": "https://notifiarr.com/api/v1/notification/passthrough/YOUR_API_KEY",
      "base_url": "",
      "start_url": "",
      "success_url": "",
      "failure_url": "",
      "payload_format": "notifiarr",
      "notifiarr_channel_id": "123456789012345678",
      "timeout_seconds": 10
    }
  }
}
```

When Notifiarr or ntfy is selected, CW always uses `url` for every event. The compatible base URL and event specific URLs are ignored.

When Pushover is selected, all URL fields are ignored.

#### Notifiarr event presentation

CW uses different notification colours:

```
Start:                                    4F8CFF
Success and recovered:                    2ECC71
Failure, Watcher stopped, sign-in lost:   FF5A5F
```

The generated Notifiarr payload contains:

```
notification.name
notification.event
discord.color
discord.ping
discord.images
discord.text
discord.ids.channel
```

The `discord.ids.channel` object is only added when a channel ID is configured.

Notification fields are limited to the first `25` fields.

#### Security

All Scheduler Webhook URL fields, the Pushover application token and the Pushover user key are treated as sensitive configuration values.

CW:

1. Encrypts these values when saving the configuration
2. Masks them in redacted configuration responses
3. Preserves the existing secret when a masked value is submitted again
4. Does not include the callback URL or the keys in logs
5. Logs only the event, the service, the host name and the delivery result

The channel ID and timeout are not secrets.

CW only sends to public addresses:

1. `localhost`, private network addresses and link local addresses are blocked
2. A host name is blocked when it resolves to such an address
3. IPv6 addresses that wrap a private IPv4 address, such as NAT64 addresses, are blocked
4. URLs that contain a user name or password are rejected

A blocked destination is logged as `blocked unsafe URL`. This is why a self hosted ntfy or webhook receiver on a private network address cannot be used.

`Send test notification` always uses the saved configuration. It does not accept a URL from the browser.

#### Logging

Successful delivery produces a log similar to:

```
[i] Scheduler webhook start: delivered
[i] Scheduler webhook success: delivered
[i] Scheduler webhook watcher_stopped: delivered
```

Failed delivery produces a log similar to:

```
[!] Scheduler webhook failure: ReadTimeout
[!] Scheduler webhook failure: ConnectionError
[!] Scheduler webhook failure: HTTPError
[!] Scheduler webhook failure: rejected by Pushover
[!] Scheduler webhook failure: blocked unsafe URL
```

A test notification writes one line:

```
Scheduler webhook test: delivered
Scheduler webhook test: failed, HTTPError
```

Only the exception type is logged. The secret URL and response body are not logged.

`delivered` means the configured receiver accepted the HTTP request. It does not guarantee that a downstream service displayed or processed the notification.

**Debug logging**

With debug mode on, CW also logs each step under the `SCHED` tag:

```
Scheduler webhook failure: sending to Pushover (api.pushover.net), timeout 10s
Scheduler webhook failure: Pushover (api.pushover.net) answered HTTP 200 in 240 ms
Scheduler webhook start: not sent, webhooks are off
Scheduler webhook success: not sent, no destination set for this event
Scheduler webhook watcher_stopped: reporting Route R1
Health check watcher_stopped: 2 checked, 1 failing, 0 unknown, 1 new, 0 recovered
Health check provider_auth_lost: 5 checked, 0 failing, 1 unknown, 0 new, 0 recovered
```

Only the service name and host name are shown. The URL path, tokens and keys are never logged.

In a health check line, `unknown` counts providers that could not be judged, for example because of a network error.

#### Troubleshooting

**The test says delivered but nothing arrives**

The test uses the saved setup. Check the `Saved setup` bar at the top of the tab. It names the service the test was sent to.

**The test button asks me to save first**

The form has unsaved changes. Save the Scheduler configuration and click the button again.

**Pushover receives nothing**

Confirm:

1. Both `Application token` and `User key` are filled in
2. Both are exactly 30 letters or digits
3. The application token belongs to an application you created in Pushover
4. The configuration was saved after entering the keys
5. The logs do not show `rejected by Pushover`

**ntfy receives nothing**

Confirm:

1. The URL is the full topic URL, including the topic name
2. The server is reachable on a public address
3. Your phone is subscribed to the same topic on the same server
4. The logs do not show `blocked unsafe URL`

**No Watcher or provider sign-in alerts arrive**

Confirm:

1. `Enable webhooks` is on
2. The alert is turned on in step 3
3. With `CW JSON`, an `All events URL` is configured
4. The problem has lasted for at least two checks
5. The provider is used by an enabled sync pair, Watcher route or scrobble webhook

**I get a Watcher alert after stopping the Watcher myself**

This is expected. An enabled route that is not running is always reported. Disable the route, switch the Watcher source off, or turn off `Watcher stops` to prevent it.

**A provider is offline but no alert is sent**

This is expected. Only a lost sign-in is reported. Outages and network errors are ignored.

**Healthchecks.io reports every event as successful**

The Ping URL was probably entered in `All events URL`.

Move it to `Compatible base URL` and clear `All events URL`.

**Healthchecks.io receives no events**

Confirm:

1. Scheduler Webhooks are enabled
2. The URL is in `Compatible base URL`
3. The URL starts with `http://` or `https://`
4. The run is a standard schedule, advanced timed sync or recurring workflow step
5. The Scheduler logs contain a delivery result

**The custom receiver cannot determine the event**

Use the `event` or `status` value in the JSON body.

Possible values are:

```
event: start
status: started

event: success
status: ok

event: failure
status: failed

event: watcher_stopped
status: failed

event: watcher_recovered
status: ok

event: provider_auth_lost
status: failed

event: provider_auth_recovered
status: ok

event: test
status: ok
```

**Notifiarr rejects the request**

Confirm:

1. The selected service is `Notifiarr`
2. The URL is the Notifiarr Passthrough endpoint
3. The complete API key is present in the URL
4. The optional channel ID contains only numbers
5. The timeout is long enough for the Notifiarr request

**Webhook delivery fails but the sync succeeds**

This is expected. Scheduler Webhooks use best effort delivery and do not control the sync exit code.

**Webhooks are enabled but no destination is configured**

The UI prevents saving an enabled webhook configuration without a usable destination for the selected service.

A directly edited configuration with no usable destination results in no notification being sent.

#### Scheduling API warning

The Scheduler configuration is available through:

```
GET /api/scheduling
POST /api/scheduling
```

The POST endpoint replaces the complete `scheduling` object. Sending only a `webhooks` object can remove the existing standard and advanced Scheduler configuration.

Read the existing scheduling configuration first, update its `webhooks` block, then submit the complete scheduling object.

A test notification can be sent with:

```
POST /api/scheduling/webhooks/test
```

It takes no request body and uses the saved configuration. The response contains `ok` and `error`.
{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://wiki.crosswatch.app/crosswatch/settings/scheduling/scheduler-webhooks.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
