Docs/Integrations

Webhooks

Oh Dear can notify your application of events using webhooks. Each hook is cryptographically signed, so the request cannot be tampered with. If you receive a webhook, you can validate it to make sure it comes from us.

Getting started

Let's start by enabling webhooks and pointing them to your own endpoint.

Enable webhooks in your account

Navigate to the notification settings page, scroll down to the webhooks section and add your webhook URL.

Once your webhook is configured, we'll call it for every event we fire. You get the raw payload and can act on it as you see fit.

How the webhooks work

Every event we fire internally, will also be translated to the webhook URL you provide in your team settings page in the account.

This means you can receive the raw payload of events like site up/down, certificate changes, ... you name it. You can then use that information to update internal systems, escalate alerts, log events, etc.

Our webhook works by firing a POST request to the endpoint you specified. All data related to the event that just took place will be inside the POST payload. For specific examples of each payload, have a look at the different webhook events.

Authentication

All webhooks we send will be signed by a signing secret, unique to your team. You can find the signing secret in your account in the team settings.

It'll be displayed as Web hook signing secret: YoUrSeCreT.

You don't have to validate the incoming request, but it's highly suggested.

Webhook retries

If we receive an HTTP/200 from your webhook URL, we consider the webhook successful. If your application returns anything else, including 301 or 302 redirects, we mark the webhook as failed and will resend the same payload again.

We will try to send the webhook up to 3 times. If we receive a non-HTTP/200 response code, or a timeout (of 3 seconds or more) for 3 times, we consider the webhook failed and will not resend that particular event.

We do not disable webhooks because they failed a couple of times, we'll only disable them if you remove the URL from your account page.

Webhook authentication & signing

Our signing method is simple but efficient. For every webhook we call, we pass an additional header called OhDear-Signature that contains the hash of the payload.

In your webhook, you can validate if that OhDear-Signature header contains the hash you expected.

It's calculated like this:

$computedSignature = hash_hmac('sha256', $payload, $secret);

The $payload is the body of the POST request, which will be a JSON representation of the event.

The $secret is the one you can find on your team notifications settings page

The hash_hmac() function is a PHP function that generates a keyed hash value using the HMAC method.

The $computedSignature should match the Ohdear-Signature that's been set. If you use our laravel package, the signature checking is handled automatically.

Laravel package

We offer a Laravel package that package can help you handle our webhooks. Out of the box it will verify the Oh Dear signature of all incoming requests. You can easily define jobs or events that should be dispatched when specific events hit your app. ``

Webhook events

There's a lot of data on this page, you can jump directly to your desired payload with the table of contents shown below.

Generic payloads

All webhook data will contain a set of recurring data points.

First, every webhook will have a uuid property. If your server was down, then Oh Dear will automatically retry sending the webhook. On the webhook log screen you can also resend a sent webhook. For both the automatic and manually retry, the uuid property will contain the same value as the original webhook.

Next, there's confirmation about which event you're receiving and which date this was generated.

{
    "uuid": "",
    "type":"uptimeCheckFailedNotification",
    "date_time":"20180312092737",
    ...
}

There are details on the site that this event took place on. This will look like this:

{
    ...
    "site":{
        "id":1,
        "url":"https:\/\/yoursite.tld",
        "uses_https":true,
        "sort_url":"yoursite.tld",
        "label": "my-site",
        "team_id":1,
        "latest_run_date":"2018-03-12 20:27:34",
        "summarized_check_result":"failed",
        "created_at":"2018-03-12 20:24:54",
        "updated_at":"2018-03-12 20:24:54",
        "checks":[
            {
                "id":1,
                "type":"uptime",
                "enabled":true,
                "human_readable_check_type":"Uptime",
                "site_id":1,
                "latest_run_result":"failed",
                "latest_run_ended_at":"2018-03-12 20:27:34",
                "latest_run_id":2,
                "latest_run_report_url":"https:\/\/ohdear.app\/sites\/1\/history\/runs\/1",
                "always_running":false
            },
            ...
        ]
    },
}

Additionally, there is meta-data present about the time the event took place.

"run":{
    ...
    "started_at":"2018-03-12 20:27:33",
    "ended_at":"2018-03-12 20:27:34",
    "created_at":"2018-03-12 20:27:31",
    "updated_at":"2018-03-12 20:27:34",
}

The actual payload of each individual event can be found in the run block, which contains all relevant data related to the event that just happened. Each of those blocks will be updated in more detail below.

Site added

Whenever you add a site to your Oh Dear account, we'll send a webhook for the siteAddedNotification type with this data.

Included in the payload are details like the site ID and the checks that were included and their IDs. The checks will still be pending, but you can use the check IDs to retrieve their latest details using our API.

{
   "type":"siteAddedNotification",
   "date_time":"20191001113659",
   "site":{
      "id":1,
      "url":"https:\/\/yoursite.tld",
      "uses_https":true,
      "sort_url":"yoursite.tld",
      "label":"yoursite.tld",
      "team_id":1,
      "latest_run_date":null,
      "summarized_check_result":"pending",
      "created_at":"2019-10-01 09:36:59",
      "updated_at":"2019-10-01 09:36:59",
      "checks":[
         {
            "id":39675,
            "type":"uptime",
            "enabled":true,
            "human_readable_check_type":"Uptime",
            "site_id":1,
            "latest_run_result":"pending",
            "latest_run_ended_at":null,
            "latest_run_id":null,
            "latest_run_report_url":"https:\/\/ohdear.app\/sites\/1\/history\/runs",
            "always_running":false
         },
         {
            "id":39676,
            "type":"broken_links",
            "enabled":true,
            "human_readable_check_type":"Broken links",
            "site_id":1,
            "latest_run_result":"pending",
            "latest_run_ended_at":null,
            "latest_run_id":null,
            "latest_run_report_url":"https:\/\/ohdear.app\/sites\/1\/history\/runs",
            "always_running":false
         },
         {
            "id":39677,
            "type":"mixed_content",
            "enabled":true,
            "human_readable_check_type":"Mixed content",
            "site_id":1,
            "latest_run_result":"pending",
            "latest_run_ended_at":null,
            "latest_run_id":null,
            "latest_run_report_url":"https:\/\/ohdear.app\/sites\/1\/history\/runs",
            "always_running":false
         },
         {
            "id":39678,
            "type":"certificate_health",
            "enabled":true,
            "human_readable_check_type":"Certificate health",
            "site_id":1,
            "latest_run_result":"pending",
            "latest_run_ended_at":null,
            "latest_run_id":null,
            "latest_run_report_url":"https:\/\/ohdear.app\/sites\/1\/history\/runs",
            "always_running":false
         },
         {
            "id":39679,
            "type":"certificate_transparency",
            "enabled":true,
            "human_readable_check_type":"Certificate transparency",
            "site_id":1,
            "latest_run_result":null,
            "latest_run_ended_at":null,
            "latest_run_id":null,
            "latest_run_report_url":"https:\/\/ohdear.app\/sites\/1\/history\/runs",
            "always_running":true
         }
      ]
   }
}

Uptime monitor: downtime detected

When we detect your website is down, we'll send a webhook for the uptimeCheckFailed type with this data.

{
   "type":"uptimeCheckFailedNotification",
   "date_time":"20180312092737",
   "site":{
      "id":1,
      ... /* See above */
   },
   "run":{
      "id":19830466,
      "check_id":10988,
      "parameters":null,
      "result":"failed",
      "result_payload":{
         "checkerResult1":{
            "checkerServer":{
               "name":"uptime-checker-paris",
               "city":"Paris",
               "country":"France",
               "ip":"45.32.146.84"
            },
            "response":[

            ],
            "lookForString":"",
            "lookForStringFound":true,
            "error":{
               "type":"connectionError",
               "errorNumber":6,
               "description":"Could not resolve host: yoursite.tld"
            },
            "statistics":{
               "url":"https:\/\/yoursite.tld\/",
               "content_type":null,
               "http_code":0,
               "header_size":0,
               "request_size":0,
               "filetime":-1,
               "ssl_verify_result":0,
               "redirect_count":0,
               "total_time":7.6000000000000004184153024056058711721561849117279052734375e-5,
               "namelookup_time":0,
               "connect_time":0,
               "pretransfer_time":0,
               "size_upload":0,
               "size_download":0,
               "speed_download":0,
               "speed_upload":0,
               "download_content_length":-1,
               "upload_content_length":-1,
               "starttransfer_time":0,
               "redirect_time":0,
               "redirect_url":"",
               "primary_ip":"",
               "certinfo":[

               ],
               "primary_port":0,
               "local_ip":"",
               "local_port":0
            }
         },
         "checkerResult2":{
            "checkerServer":{
               "name":"uptime-checker-bangalore",
               "city":"Bangalore",
               "country":"India",
               "ip":"139.59.75.39"
            },
            "response":[

            ],
            "lookForString":"",
            "lookForStringFound":true,
            "error":{
               "type":"connectionError",
               "errorNumber":6,
               "description":"Could not resolve host: yoursite.tld"
            },
            "statistics":{
               "url":"https:\/\/yoursite.tld\/",
               "content_type":null,
               "http_code":0,
               "header_size":0,
               "request_size":0,
               "filetime":-1,
               "ssl_verify_result":0,
               "redirect_count":0,
               "total_time":6.600000000000000506018837942434629439958371222019195556640625e-5,
               "namelookup_time":0,
               "connect_time":0,
               "pretransfer_time":0,
               "size_upload":0,
               "size_download":0,
               "speed_download":0,
               "speed_upload":0,
               "download_content_length":-1,
               "upload_content_length":-1,
               "starttransfer_time":0,
               "redirect_time":0,
               "redirect_url":"",
               "primary_ip":"",
               "certinfo":[

               ],
               "primary_port":0,
               "local_ip":"",
               "local_port":0
            }
         }
      },
   }
}

Uptime monitor: recovery detected

When site comes back up, we'll send a webhook for the uptimeCheckRecovered type with this data.

{
   "type":"uptimeCheckRecoveredNotification",
   "date_time":"20191001112131",
   "run":{
      "id":423131444,
      "check_id":33859,
      "parameters":[

      ],
      "result":"succeeded",
      "result_payload":{
         "checkerResult1":{
            "checkerServer":{
               "name":"uptime-checker-paris",
               "city":"Paris",
               "country":"France",
               "ip":"45.32.146.84"
            },
            "response":{
               "code":200,
               "code_phrase":"OK",
               "protocol":"1.1",
               "headers":{
                  "Server":[
                     "nginx"
                  ],
                  "Date":[
                     "Tue, 01 Oct 2019 09:21:30 GMT"
                  ],
                  "Content-Type":[
                     "text\/html; charset=UTF-8"
                  ],
                  "Transfer-Encoding":[
                     "chunked"
                  ],
                  "Connection":[
                     "keep-alive"
                  ],
                  "Vary":[
                     "Accept-Encoding",
                     "Accept-Encoding"
                  ],
                  "Cache-Control":[
                     "private, must-revalidate"
                  ],
                  "pragma":[
                     "no-cache"
                  ],
                  "expires":[
                     "-1"
                  ],
                  "Set-Cookie":[
                     "XSRF-TOKEN=eyJpdiI6InlNSkJOQVpwb2sra0c2Y0tsRkp5TEE9PSIsInZhbHVlIjoiUXVweXRIWXlxT1Q0U0hrUmUyTTh2K3lXd1EyM1VkcGwzUG1RN2g4a2tVcXpITU5ZVitGU05QRTlhWFFvTTQzdWpmZlFSbk9kYWZoR2V5ZTc1dVR0QlE9PSIsIm1hYyI6IjNkZDIyMGU2ZDMzOGJkMmYzMGU1N2QwNmNiOWE0MmJmODc3YWRjZGQ4NWEwY2IyYjE0ZGQ1ZTA1YjI2NWQwYzgifQ%3D%3D; expires=Tue, 01-Oct-2019 11:21:30 GMT; Max-Age=7200; path=\/",
                     "laravel_session=eyJpdiI6IkxLVUFIY1BWMHE2bGNuTTFGYkp1SkE9PSIsInZhbHVlIjoiZGhHZFlIOHBpbTFSWGM4RFgwcFlwUlwvMThlUkFKYUdqU0tGRVVldGNDY3ZwRXc5dEh4K1wvWVY1MFk0R2UxZU5kVk9CMFppYkpBdStUVEFKb1FCR3RRZz09IiwibWFjIjoiZGI2ZjdjYjgxY2ZiZTQ1MGNiYzAzMzE1ZWNlNjdlMjExNWE3MjFhZTRiMWZhYTcwYjExMDVjMTNkYzRkZjFlYiJ9; expires=Tue, 01-Oct-2019 11:21:30 GMT; Max-Age=7200; path=\/; httponly"
                  ]
               }
            },
            "lookForString":"",
            "lookForStringFound":true,
            "error":[

            ],
            "statistics":{
               "url":"https:\/\/immutable.be\/",
               "content_type":"text\/html; charset=UTF-8",
               "http_code":200,
               "header_size":1024,
               "request_size":152,
               "filetime":-1,
               "ssl_verify_result":0,
               "redirect_count":0,
               "total_time":0.110265000000000001900701818158267997205257415771484375,
               "namelookup_time":0.0042430000000000002435829316027593449689447879791259765625,
               "connect_time":0.01140900000000000073796524446834155241958796977996826171875,
               "pretransfer_time":0.058913000000000000089261931179862585850059986114501953125,
               "size_upload":0,
               "size_download":3227,
               "speed_download":29265,
               "speed_upload":0,
               "download_content_length":-1,
               "upload_content_length":-1,
               "starttransfer_time":0.10999699999999999755484481056555523537099361419677734375,
               "redirect_time":0,
               "redirect_url":"",
               "primary_ip":"31.193.180.217",
               "certinfo":[

               ],
               "primary_port":443,
               "local_ip":"45.32.146.84",
               "local_port":36260
            }
         }
      },
      ...
   },
   "site":{
      "id":1,
      ... /* See above */
   }
}

Performance threshold exceeded

If the site is continuously slower than the performance threshold in your site settings we will send a performanceThresholdExceededNotification. See performance docs for more details about this check.

{
    "type": "performanceThresholdExceededNotification",
    "uuid": "",
    "date_time": "20220414070203",
    "run": {
        "id": 12345678901,
        "result": "failed",
        "check_id": 123456,
        "ended_at": "2022-04-14T09:00:00.000000Z",
        "created_at": "2022-04-14T09:00:00.000000Z",
        "parameters": null,
        "started_at": null,
        "updated_at": "2022-04-14T09:00:00.000000Z",
        "result_payload": {
            "performance_threshold_in_ms": "3500",
            "performance_change_percentage_threshold": "50",
            "average_total_time_past_15_minutes_in_ms": "4139"
        },
        "check": {
            "id": 123456,
            "site":{
                "id":1,
                ... /* See above */
            },
            "type": "performance",
            "enabled": true,
            "site_id": ,
            "created_at": "2022-01-01T18:00:00.000000Z",
            "parameters": null,
            "updated_at": "2022-04-14T08:15:00.000000Z",
            "order_column": 1,
            "latest_run_id": 987654321234,
            "latest_run_result": "failed",
            "latest_run_ended_at": "2022-04-14T09:00:00.000000Z",
            "latest_completed_run_id": 987654321234,
            "latest_completed_run_result": "failed",
            "latest_completed_run_ended_at": "2022-04-14T09:00:00.000000Z"
        }
    },
    "site":{
        "id":1,
        ... /* See above */
     }
}

Performance threshold recovered

If the site starts performing quickly again a performanceThresholdRecoveredNotification will be triggered. See performance docs for more details about this check.

{
    "type": "performanceThresholdRecoveredNotification",
    "uuid": "",
    "date_time": "20220414074621",
    "run": {
        "id": 12345678901,
        "result": "succeeded",
        "check_id": 123456,
        "ended_at": "2022-04-14T09:00:00.000000Z",
        "created_at": "2022-04-14T09:00:00.000000Z",
        "parameters": null,
        "started_at": null,
        "updated_at": "2022-04-14T09:00:00.000000Z",
        "result_payload": {
            "performance_threshold_in_ms": "3500",
            "performance_change_percentage_threshold": "50",
            "average_total_time_past_15_minutes_in_ms": "3452"
        },
        "check": {
            "id": 123456,
            "site":{
                "id":1,
                ... /* See above */
            },
            "type": "performance",
            "enabled": true,
            "site_id": 1,
            "created_at": "2022-01-10T14:30:00.000000Z",
            "parameters": null,
            "updated_at": "2022-04-10T14:30:00.000000Z",
            "order_column": 1,
            "latest_run_id": 12345678901,
            "latest_run_result": "succeeded",
            "latest_run_ended_at": "2022-04-14T09:00:00.000000Z",
            "latest_completed_run_id": 12345678901,
            "latest_completed_run_result": "succeeded",
            "latest_completed_run_ended_at": "2022-04-14T09:00:00.000000Z"
        }
    },
    "site":{
        "id":1,
        ... /* See above */
    }
}

Performance Delta Exceeded

If the site starts performing slower or faster than the performance change percentage in your site settings a performanceDeltaExceededNotification will be triggered. See performance docs for more details about this check.

{
    "type": "performanceDeltaExceededNotification",
    "uuid": "",
    "date_time": "20220414110128",
    "run": {
        "id": 12345678901,
        "result": "warning",
        "check_id": 65853,
        "ended_at": "2022-04-14T09:00:00.000000Z",
        "created_at": "2022-04-14T09:00:00.000000Z",
        "parameters": null,
        "started_at": null,
        "updated_at": "2022-04-14T09:00:00.000000Z",
        "result_payload": {
            "performance_threshold_in_ms": "3500",
            "actual_performance_change_percentage": "-73",
            "average_total_time_past_2_hours_in_ms": "122",
            "performance_change_percentage_threshold": "50",
            "average_total_time_past_15_minutes_in_ms": "32"
        },
        "check": {
            "id": 12345,
            "site":{
                "id":1,
                ... /* See above */
            },
            "type": "performance",
            "enabled": true,
            "site_id": 1,
            "created_at": "2022-04-14T08:00:00.000000Z",
            "parameters": null,
            "updated_at": "2022-04-14T08:00:00.000000Z",
            "order_column": 1,
            "latest_run_id": 98765432101,
            "latest_run_result": "warning",
            "latest_run_ended_at": "2022-04-14T08:00:00.000000Z",
            "latest_completed_run_id": 98765432101,
            "latest_completed_run_result": "warning",
            "latest_completed_run_ended_at": "2022-04-14T08:00:00.000000Z"
        }
    },
    "site":{
        "id":1,
        ... /* See above */
    }
}

Whenever a crawl of your site is finished, and broken links were found, we'll send a webhook for the brokenLinksFound type with this data.

{
   "type":"brokenLinksFoundNotification",
   "date_time":"20191001114254",
   "run":{
      "id":423155781,
      "check_id":39681,
      "parameters":[

      ],
      "result":"failed",
      "result_payload":{
         "broken_links":[
            {
               "crawled_url":"https:\/\/immutable.be\/broken-links-test-page\/0\/404",
               "status_code":404,
               "found_on_url":"https:\/\/immutable.be\/broken-links-test-page\/"
            },
            {
               "crawled_url":"https:\/\/immutable.be\/broken-links-test-page\/1\/404",
               "status_code":404,
               "found_on_url":"https:\/\/immutable.be\/broken-links-test-page\/"
            },
            ...
         ],
         "crawled_urls":[
            {
               "crawled_url":"https:\/\/immutable.be\/broken-links-test-page\/",
               "status_code":200,
               "found_on_url":""
            },
            {
               "crawled_url":"https:\/\/immutable.be\/broken-links-test-page\/0\/404",
               "status_code":404,
               "found_on_url":"https:\/\/immutable.be\/broken-links-test-page\/"
            },
            ...
         ],
         "whitelist":null
      },
      ...
   },
   "site":{
      "id":1,
      ... /* see above */
   }
}

When we detect that all broken links have been fixed, we'll send a webhook for the brokenLinksFixed type with this data.

{
   "type":"brokenLinksFixedNotification",
   "date_time":"20191001114436",
   "run":{
      "id":423159529,
      "check_id":39681,
      "parameters":[

      ],
      "result":"succeeded",
      "result_payload":{
         "broken_links":[

         ],
         "crawled_urls":[
            {
               "crawled_url":"https:\/\/immutable.be\/broken-links-test-page\/",
               "status_code":200,
               "found_on_url":""
            },
            {
               "crawled_url":"https:\/\/immutable.be\/broken-links-test-page\/?0-404",
               "status_code":200,
               "found_on_url":"https:\/\/immutable.be\/broken-links-test-page\/"
            },
            ...
         ],
         "whitelist":null
      },
      ...
   },
   "site":{
      "id":1,
      ... /* see above */
   }
}

Mixed content found

When we detected mixed content, we'll send a webhook for the mixedContentFound type with this data.

There's a special note here: the result_payload contains a serialized array with instances of the Spatie\MixedContentScanner\MixedContent model.

To use, first unserialize() the data and treat each element in the resulting array as a model of MixedContent.

{
   "type":"mixedContentFoundNotification",
   "date_time":"20191001114652",
   "run":{
      "id":423161645,
      "check_id":39687,
      "parameters":[

      ],
      "result":"failed",
      "result_payload":{
         "foundMixedContent":"a:9:{i:0;O:39:\"Spatie\\MixedContentScanner\\MixedContent\":3:{s:11:\"elementName\";s:3:\"img\";s:15:\"mixedContentUrl\";O:19:\"GuzzleHttp\\Psr7\\Uri\":7:{s:27:\"\u0000GuzzleHttp\\Psr7\\Uri\u0000scheme\";s:4:\"http\";s:29:\"\u0000GuzzleHttp\\Psr7\\Uri\u0000userInfo\";s:0:\"\";s:25:\"\u0000GuzzleHttp\\Psr7\\Uri\u0000host\";s:16:\"doesnotexist.tld\";s:25:\"\u0000GuzzleHttp\\Psr7\\Uri\u0000port\";N;s:25:\"\u0000GuzzleHttp\\Psr7\\Uri\u0000path\";s:11:\"\/image0.png\";s:26:\"\u0000GuzzleHttp\\Psr7\\Uri\u0000query\";s:0:\"\";s:29:\"\u0000GuzzleHttp\\Psr7\\Uri\u0000fragment\";s:0:\"\";}s:10:\"foundOnUrl\";O:19:\"GuzzleHttp\\Psr7\\Uri\":7:{s:27:\"\u0000GuzzleHttp\\Psr7\\Uri\u0000scheme\";s:5:\"https\";s:29:\"\u0000GuzzleHttp\\Psr7\\Uri\u0000userInfo\";s:0:\"\";...",
         "pagesWithoutMixedContent":0,
         "crawledPages":"a:1:{i:0;s:45:\"https:\/\/site.tld\/\";}"
      },
      ...
   },
   "site":{
      "id":1,
      ... /* see above */
   }
}

Mixed content fixed

When the mixed content was found to be fixed, we'll send a webhook for the mixedContentFixed type with this data.

{
   "type":"mixedContentFixedNotification",
   "date_time":"20191001115320",
   "run":{
      "id":423169009,
      "check_id":39687,
      "parameters":[

      ],
      "result":"succeeded",
      "result_payload":{
         "foundMixedContent":"a:0:{}",
         "pagesWithoutMixedContent":1,
         "crawledPages":"a:1:{i:0;s:45:\"https:\/\/site.tld\/\";}"
      },
      ...
   },
   "site":{
      "id":1,
      ... /* see above */
   }
}

Certificate expires soon

By default we will send you a certificateExpiresSoonNotification if the certificate for the site expires within 7 days for Let's Encrypt certificates and 14 days for all others, unless you have set the threshold in days in your site certificate settings.

See certificate docs for more details about this check.

{
    "type": "certificateExpiresSoonNotification",
    "uuid": "",
    "date_time": "20220411063354",
    "run": {
        "id": 12345678901,
        "result": "warning",
        "check_id": 123456,
        "ended_at": "2022-04-14T09:00:00.000000Z",
        "created_at": "2022-04-14T09:00:00.000000Z",
        "parameters": null,
        "started_at": null,
        "updated_at": "2022-04-14T09:00:00.000000Z",
        "result_payload": {
            "issues": {
                "expiresSoon": "Certificate for  will expire in 5 days."
            },
            "certificateChain": {
                "hostName": "www.example.com",
                "certificates": {
                    "0": {
                        "fingerprint": "0a1376d4347e0639d6fa7ab9174eb61a0eb8dfe2",
                        "remoteAddress": "23.62.99.210:443",
                        "fingerprintSha256": "6c28cdd8deb191be72a9e3f24b5f1d36d95ed3bb8e3fd5a1e8947b4bba247986",
                        "rawCertificateFields": {
                            "hash": "2a73b43c",
                            "name": "/CN=le0413.secure.dealer.com",
                            "issuer": {
                                "C": "US",
                                "O": "Let's Encrypt",
                                "CN": "R3"
                            },
                            "subject": { "CN": "le0413.secure.dealer.com" },
                            "validTo": "220417181740Z",
                            "version": 2,
                            "purposes": {
                                "1": [true, false, "sslclient"],
                                "2": [true, false, "sslserver"],
                                "3": [true, false, "nssslserver"],
                                "4": [false, false, "smimesign"],
                                "5": [false, false, "smimeencrypt"],
                                "6": [false, false, "crlsign"],
                                "7": [true, true, "any"],
                                "8": [true, false, "ocsphelper"],
                                "9": [false, false, "timestampsign"]
                            },
                            "validFrom": "220117181741Z",
                            "extensions": {
                                "keyUsage": "Digital Signature, Key Encipherment",
                                "subjectAltName": "DNS:example.com, DNS:example2.com",
                                "ct_precert_scts": "...",
                                "basicConstraints": "CA:FALSE",
                                "extendedKeyUsage": "TLS Web Server Authentication, TLS Web Client Authentication",
                                "authorityInfoAccess": "...\n...\n",
                                "certificatePolicies": "Policy: ...\n...",
                                "subjectKeyIdentifier": "21:5A:...",
                                "authorityKeyIdentifier": "keyid:...\n"
                            },
                            "serialNumber": "0x4f682044656172",
                            "validTo_time_t": 1650219460,
                            "serialNumberHex": "4f682044656172",
                            "signatureTypeLN": "sha256WithRSAEncryption",
                            "signatureTypeSN": "RSA-SHA256",
                            "signatureTypeNID": 668,
                            "validFrom_time_t": 1642443461
                        }
                    },
                    "2": {
                        "fingerprint": "0a1376d4347e0639d6fa7ab9174eb61a0eb8dfe2",
                        "remoteAddress": "23.62.99.210:443",
                        "fingerprintSha256": "6c28cdd8deb191be72a9e3f24b5f1d36d95ed3bb8e3fd5a1e8947b4bba247986",
                        "rawCertificateFields": {
                            "hash": "9d33f237",
                            "name": "/C=US/O=Let's Encrypt/CN=R3",
                            "issuer": {
                                "C": "US",
                                "O": "Internet Security Research Group",
                                "CN": "ISRG Root X1"
                            },
                            "subject": {
                                "C": "US",
                                "O": "Let's Encrypt",
                                "CN": "R3"
                            },
                            "validTo": "250915160000Z",
                            "version": 2,
                            "purposes": {
                                "1": [true, true, "sslclient"],
                                "2": [true, true, "sslserver"],
                                "3": [false, true, "nssslserver"],
                                "4": [false, false, "smimesign"],
                                "5": [false, false, "smimeencrypt"],
                                "6": [true, true, "crlsign"],
                                "7": [true, true, "any"],
                                "8": [true, true, "ocsphelper"],
                                "9": [false, true, "timestampsign"]
                            },
                            "validFrom": "200904000000Z",
                            "extensions": {
                                "keyUsage": "Digital Signature, Certificate Sign, CRL Sign",
                                "basicConstraints": "CA:TRUE, pathlen:0",
                                "extendedKeyUsage": "TLS Web Client Authentication, TLS Web Server Authentication",
                                "authorityInfoAccess": "CA Issuers - URI:http://x1.i.lencr.org/\n",
                                "certificatePolicies": "Policy: ...\n",
                                "subjectKeyIdentifier": "14:2E:...",
                                "crlDistributionPoints": "\nFull Name:\n  URI:http://x1.c.lencr.org/\n",
                                "authorityKeyIdentifier": "keyid:79:B4:...\n"
                            },
                            "serialNumber": "0x4f682044656172",
                            "validTo_time_t": 1757952000,
                            "serialNumberHex": "4f682044656172",
                            "signatureTypeLN": "sha256WithRSAEncryption",
                            "signatureTypeSN": "RSA-SHA256",
                            "signatureTypeNID": 668,
                            "validFrom_time_t": 1599177600
                        }
                    },
                    "3": {
                        "fingerprint": "0a1376d4347e0639d6fa7ab9174eb61a0eb8dfe2",
                        "remoteAddress": "23.62.99.210:443",
                        "fingerprintSha256": "6c28cdd8deb191be72a9e3f24b5f1d36d95ed3bb8e3fd5a1e8947b4bba247986",
                        "rawCertificateFields": {
                            "hash": "3032bcee",
                            "name": "/C=US/O=Internet Security Research Group/CN=ISRG Root X1",
                            "issuer": {
                                "O": "Digital Signature Trust Co.",
                                "CN": "DST Root CA X3"
                            },
                            "subject": {
                                "C": "US",
                                "O": "Internet Security Research Group",
                                "CN": "ISRG Root X1"
                            },
                            "validTo": "240930181403Z",
                            "version": 2,
                            "purposes": {
                                "1": [false, true, "sslclient"],
                                "2": [false, true, "sslserver"],
                                "3": [false, true, "nssslserver"],
                                "4": [false, true, "smimesign"],
                                "5": [false, true, "smimeencrypt"],
                                "6": [true, true, "crlsign"],
                                "7": [true, true, "any"],
                                "8": [true, true, "ocsphelper"],
                                "9": [false, true, "timestampsign"]
                            },
                            "validFrom": "210120191403Z",
                            "extensions": {
                                "keyUsage": "Certificate Sign, CRL Sign",
                                "basicConstraints": "CA:TRUE",
                                "authorityInfoAccess": "CA Issuers - URI:http://apps.identrust.com/roots/dstrootcax3.p7c\n",
                                "certificatePolicies": "Policy: ...\n",
                                "subjectKeyIdentifier": "79:B4:...",
                                "crlDistributionPoints": "\nFull Name:\n  URI:http://crl.identrust.com/DSTROOTCAX3CRL.crl\n",
                                "authorityKeyIdentifier": "keyid:C4:A7:...\n"
                            },
                            "serialNumber": "950482111265633317569109389149956118711",
                            "validTo_time_t": 1727720043,
                            "serialNumberHex": "4f682044656172",
                            "signatureTypeLN": "sha256WithRSAEncryption",
                            "signatureTypeSN": "RSA-SHA256",
                            "signatureTypeNID": 668,
                            "validFrom_time_t": 1611170043
                        }
                    }
                },
                "couldConnectToHost": true
            }
        },
        "check": {
            "id": 123456,
            "site":{
                "id":1,
                ... /* see above */
            },
            "type": "certificate_health",
            "enabled": true,
            "site_id": 1,
            "created_at": "2022-04-14T09:00:00.000000Z",
            "parameters": null,
            "updated_at": "2022-04-14T09:00:00.000000Z",
            "order_column": 5,
            "latest_run_id": 10245160027,
            "latest_run_result": "warning",
            "latest_run_ended_at": "2022-04-14T09:00:00.000000Z",
            "latest_completed_run_id": 10245160027,
            "latest_completed_run_result": "warning",
            "latest_completed_run_ended_at": "2022-04-14T09:00:00.000000Z"
        }
    },
    "site":{
        "id":1,
        ... /* see above */
    }
}

Certificate found to be unhealthy

When we detect issues with your certificate, we'll send a webhook for the certificateUnhealthy type with this data.

There's a special note here: the result_payload contains a serialized array with instances of the certificateChain model.

To use, first unserialize() the data and treat each element in the resulting array as a model of SslCertificate.

You'll find most things you need in the issues[] array, which contains an array of strings with human-readable notices about the problems we detected.

{
   "type":"certificateUnhealthyNotification",
   "date_time":"20191001115620",
   "run":{
      "id":423172975,
      "check_id":39698,
      "parameters":[

      ],
      "result":"failed",
      "result_payload":{
         "issues":{
            "coversWrongDomain":"Certificate does not cover `https:\/\/neverssl.com\/` but `*.cloudfront.net`."
         },
         "certificateChain":"O:68:\"App\\Domain\\Check\\Checkers\\CertificateHealth\\CertificateChain\":4:{s:7:\"\u0000*\u0000site\";s:21:\"https:\/\/neverssl.com\/\";s:11:\"\u0000*\u0000hostName\";s:12:\"neverssl.com\";s:15:\"\u0000*\u0000certificates\";O:29:\"Illuminate\\Support\\Collection\":1:{s:8:\"\u0000*\u0000items\";a:3:{i:0;
         ..."
      },
      ...
   },
   "site":{
      "id":1,
      ... /* see above */
   }
}

Certificate changed

If we detect a changed certificate on the site we are monitoring, you will be reported with any issues, certificate chain and the certificate changes. This way, you can verify that all domains that were previously covered by the certificate are still present.

See certificate docs for more details about this check.

{
    "type": "certificateHasChangedNotification",
    "uuid": "",
    "date_time": "20220414044921",
    "run": {
        "id": 12345678901,
        "result": "warning",
        "check_id": 122329,
        "ended_at": "2022-04-14T09:00:00.000000Z",
        "created_at": "2022-04-14T09:00:00.000000Z",
        "parameters": null,
        "started_at": null,
        "updated_at": "2022-04-14T00:00:0.000000Z",
        "result_payload": {
            "issues": { "hasChanged": "The certificate has changed" },
            "certificateChain": {
                ... /** see above **/
            },
            "certificateChanges": [
                {
                    "hash": {
                        "new": "baf82b8f0e93086c28b6688c09c6f7bd615bc7f3",
                        "old": "8bdf3f5704493304de7f595ec72ffa80bf095dff"
                    },
                    "expirationDate": {
                        "new": "2022-07-12 23:10:16",
                        "old": "2022-05-13 23:10:30"
                    }
                }
            ]
        },
        "check": {
            "id": 123456,
            "site":{
                "id":1,
                ... /* see above */
            },
            "type": "certificate_health",
            "enabled": true,
            "site_id": 20140,
            "created_at": "2022-04-14T09:00:00.000000Z",
            "parameters": null,
            "updated_at": "2022-04-14T09:00:00.000000Z",
            "order_column": 5,
            "latest_run_id": 10298733707,
            "latest_run_result": "warning",
            "latest_run_ended_at": "2022-04-14T09:00:00.000000Z",
            "latest_completed_run_id": 10298733707,
            "latest_completed_run_result": "warning",
            "latest_completed_run_ended_at": "2022-04-14T09:00:00.000000Z"
        }
    },
    "site":{
        "id":1,
        ... /* see above */
    }
}

Certificate fixed

When we detect that issues with a certificate have been fixed, we'll send a webhook of type certificateFixed.

There's a special note here: the result_payload contains a serialized array with instances of the certificateChain model.

To use, first unserialize() the data and treat each element in the resulting array as a model of SslCertificate.

{
   "type":"certificateFixedNotification",
   "date_time":"20191001115620",
   "run":{
      "id":423172975,
      "check_id":39698,
      "parameters":[

      ],
      "result":"succeeded",
      "result_payload":{
         "certificateChain":"O:68:\"App\\Domain\\Check\\Checkers\\CertificateHealth\\CertificateChain\":4:{s:7:\"\u0000*\u0000site\";s:21:\"https:\/\/neverssl.com\/\";s:11:\"\u0000*\u0000hostName\";s:12:\"neverssl.com\";s:15:\"\u0000*\u0000certificates\";O:29:\"Illuminate\\Support\\Collection\":1:{s:8:\"\u0000*\u0000items\";a:3:{i:0;
         ..."
      },
      ...
   },
   "site":{
      "id":1,
      ... /* see above */
   }
}

DNS issues found

{
    "type": "dnsIssuesFoundNotification",
    "uuid": "",
    "date_time": "20220414100503",
    "run": {
        "id": 12345678907,
        "result": "failed",
        "check_id": 123456,
        "ended_at": "2022-04-14T09:00:00.000000Z",
        "created_at": "2022-04-14T09:00:00.000000Z",
        "parameters": null,
        "started_at": null,
        "updated_at": "2022-04-14T09:00:00.000000Z",
        "result_payload": [],
        "check": {
            "id": 123456,
            "site":{
                "id":1,
                ... /* see above */
            },
            "type": "dns",
            "enabled": true,
            "site_id": 1,
            "created_at": "2022-04-14T09:00:00.000000Z",
            "parameters": null,
            "updated_at": "2022-04-14T09:00:00.000000Z",
            "order_column": 7,
            "latest_run_id": 10241978697,
            "latest_run_result": "failed",
            "latest_run_ended_at": "2022-04-14T09:00:00.000000Z",
            "latest_completed_run_id": 10241978697,
            "latest_completed_run_result": "failed",
            "latest_completed_run_ended_at": "2022-04-14T09:00:00.000000Z"
        }
    },
    "site":{
        "id":1,
        ... /* see above */
    }
}

DNS records changed

{
    "type": "dnsRecordsChangedNotification",
    "uuid": "",
    "date_time": "20220414114047",
    "run": {
        "id": 12345678901,
        "result": "succeeded",
        "check_id": 123456,
        "ended_at": "2022-04-14T09:00:00.000000Z",
        "created_at": "2022-04-14T09:00:00.000000Z",
        "parameters": null,
        "started_at": null,
        "updated_at": "2022-04-14T09:00:00.000000Z",
        "result_payload": [],
        "check": {
            "id": 123456,
            "site":{
                "id":1,
                ... /* see above */
            },
            "type": "dns",
            "enabled": true,
            "site_id": 1,
            "created_at": "2022-04-14T09:00:00.000000Z",
            "parameters": null,
            "updated_at": "2022-04-14T09:00:00.000000Z",
            "order_column": 6,
            "latest_run_id": 12345678901,
            "latest_run_result": "succeeded",
            "latest_run_ended_at": "2022-04-14T09:00:00.000000Z",
            "latest_completed_run_id": 12345678901,
            "latest_completed_run_result": "succeeded",
            "latest_completed_run_ended_at": "2022-04-14T09:00:00.000000Z"
        }
    },
    "site":{
        "id":1,
        ... /* see above */
    }
}

DNS issues fixed

{
    "type": "dnsIssuesFixedNotification",
    "uuid": "",
    "date_time": "20220414103508",
    "run": {
        "id": 12345678901,
        "result": "succeeded",
        "check_id": 123456,
        "ended_at": "2021-04-14T09:00:00.000000Z",
        "created_at": "2021-04-14T09:00:00.000000Z",
        "parameters": null,
        "started_at": null,
        "updated_at": "2021-04-14T09:00:00.000000Z",
        "result_payload": [],
        "check": {
            "id": 123456,
            "site":{
                "id":1,
                ... /* see above */
            },
            "type": "dns",
            "enabled": true,
            "site_id": 1,
            "created_at": "2021-04-14T09:00:00.000000Z",
            "parameters": null,
            "updated_at": "2021-04-14T09:00:00.000000Z",
            "order_column": 6,
            "latest_run_id": 12345678901,
            "latest_run_result": "succeeded",
            "latest_run_ended_at": "2021-04-14T09:00:00.000000Z",
            "latest_completed_run_id": 12345678901,
            "latest_completed_run_result": "succeeded",
            "latest_completed_run_ended_at": "2021-04-14T09:00:00.000000Z"
        }
    },
    "site":{
        "id":1,
        ... /* see above */
    }
}

Scheduled task reported an error

When your code actively reports a failure state to us for a scheduled task, we fire a cronFailed event.

{
  "type": "cronFailedNotification",
  "date_time": "20191001115620",
  "uuid": "860e04a8-2025-4793-a45c-c9ad71710cf2"
}

Scheduled task didn't report on time

When we did not receive a callback on time for a schedule task, we consider it failed and will fire a cronFailed event.

Some notes:

  • result_payload is a PHP serialized array (ie: unserialize() needed to operate on the array)
  • check: contains all the details about the cron check
  • cron_check_definitions: contains all cron/scheduled task definitions tied to this particular site & check
{
  "type": "cronNotExecutedOnTimeNotification",
  "date_time": "20191001115620",
  "run": {
    "id": 5009631000,
    "check_id": 71023,
    "parameters": [],
    "result": "failed",
    "result_payload": {
      "failedCronCheckDefinitions": "a:2:{i:0;a:3:{s:2:\"id\";i:4991;s:4:\"name\";s:5:\"1-2-3\";s:6:\"result\";s:11:\"checkFailed\";}i:1;a:3:{s:2:\"id\";i:678;s:4:\"name\";s:11:\"site-update\";s:6:\"result\";s:12:\"pingFinished\";}}"
    },
    "started_at": "2021-05-07T11:16:00.000000Z",
    "ended_at": "2021-05-07T11:16:01.000000Z",
    "created_at": "2021-05-07T11:16:00.000000Z",
    "updated_at": "2021-05-07T11:16:01.000000Z",
    "check": {
      "id": 71023,
      "type": "cron",
      "...": "...",
      "site": {
        "id": 1,
        "url": "https://yoursite.tld",
        "...": "...",
      },
      "cron_check_definitions": [
         {
            /* A series of cron definitions */
         }
      ]
    }
  },
  "uuid": "6fd75f8c-bc48-4e21-9523"
}

Application health problem detected

{
    "type": "applicationHealthProblemDetectedNotification",
    "uuid": "",
    "date_time": "20220414100102",
    "run": {
        "id": 12345678901,
        "check": {
            "id": 123456,
            "site":{
                "id":1,
                ... /* see above */
            },
            "type": "application_health",
            "enabled": true,
            "site_id": 33160,
            "created_at": "2022-04-14T09:00:00.000000Z",
            "parameters": null,
            "updated_at": "2022-04-14T09:00:00.000000Z",
            "order_column": 6,
            "latest_run_id": 12345678901,
            "latest_run_result": "failed",
            "latest_run_ended_at": "2022-04-14T09:00:00.000000Z",
            "latest_completed_run_id": 12345678901,
            "latest_completed_run_result": "failed",
            "latest_completed_run_ended_at": "2022-04-14T09:00:00.000000Z"
        },
        "result": "failed",
        "check_id": 123456,
        "ended_at": "2022-04-14T09:00:00.000000Z",
        "created_at": "2022-04-14T09:00:00.000000Z",
        "parameters": null,
        "started_at": null,
        "updated_at": "2022-04-14T09:00:00.000000Z",
        "result_payload": []
    },
    "site":{
        "id":1,
        ... /* see above */
    }
}

Application health problem fixed

{
    "type": "applicationHealthProblemFixedNotification",
    "uuid": "",
    "date_time": "20220414100333",
    "run": {
        "id": 12345678901,
        "result": "succeeded",
        "check_id": 123456,
        "ended_at": "2022-04-14T09:00:00.000000Z",
        "created_at": "2022-04-14T09:00:00.000000Z",
        "parameters": null,
        "started_at": null,
        "updated_at": "2022-04-14T09:00:00.000000Z",
        "result_payload": [],
        "check": {
            "id": 123456,
            "site":{
                "id":1,
                ... /* see above */
            },
            "type": "application_health",
            "enabled": true,
            "site_id": 1,
            "created_at": "2022-03-10T10:14:58.000000Z",
            "parameters": null,
            "updated_at": "2022-04-14T09:00:00.000000Z",
            "order_column": 6,
            "latest_run_id": 12345678901,
            "latest_run_result": "succeeded",
            "latest_run_ended_at": "2022-04-14T09:00:00.000000Z",
            "latest_completed_run_id": 12345678901,
            "latest_completed_run_result": "succeeded",
            "latest_completed_run_ended_at": "2022-04-14T09:00:00.000000Z"
        }
    },
    "site":{
        "id":1,
        ... /* see above */
    }
}
Was this page helpful?

Feel free to reach out via support@ohdear.app or on Twitter via @OhDearApp if you have any other questions. We'd love to help!