Webhooks can be added on a per Site basis and allow you to receive notifications when a Snapshot is created, when a budget is exceeded, met or at risk.
To create a new webhook, navigate to Settings → Integrations → Add a Webhook for a given Site and paste in the payload URL (data will be delivered as application/json via HTTP POST).
You can choose from:
Create the integration by clicking the Save button.
Webhooks and other integrations can be managed pragmatically using the Node.js API. See the Integrations page for more information.
A webhook will be JSON (application/json) delivered via HTTP POST to an endpoint of your choosing. Here is an example of the output:
1{2 "id": 1485,3 "organisation_id": "your-organisation-id",4 "site_id": "apple",5 "primary_region_id": "us-east-1",6 "ref": null,7 "client": "web",8 "status": "completed",9 "html_url": "https://calibreapp.com/your-organisation-id/apple/snapshots/1485",10 "url": "https://calibreapp.com/api/sites/apple/snapshots/1485.json",11 "created_at": "2018-07-16T23:33:17.970Z",12 "pages": [13 {14 "uuid": "xxx-123-456-789-xxx",15 "name": "iPhone X",16 "status": "completed",17 "endpoint": "https://www.apple.com/iphone-x/",18 "canonical": false,19 "profile": "iPhone 6, 3G connection",20 "profile_uuid": "xxx-123-456-789-xxx",21 "metrics": [22 {23 "name": "speed_index",24 "value": 1409725 },26 {27 "name": "visually_complete",28 "value": 2131729 },30 {31 "name": "lighthouse-seo-score",32 "value": 7533 },34 {35 "name": "lighthouse-best-practices-score",36 "value": 7537 },38 {39 "name": "lighthouse-accessibility-score",40 "value": 8741 },42 {43 "name": "lighthouse-performance-score",44 "value": 1045 },46 {47 "name": "lighthouse-pwa-score",48 "value": 4549 },50 {51 "name": "js-parse-compile",52 "value": 688853 },54 {55 "name": "time-to-first-byte",56 "value": 12857 },58 {59 "name": "first-contentful-paint",60 "value": 443361 },62 {63 "name": "first-meaningful-paint",64 "value": 1164965 },66 {67 "name": "firstRender",68 "value": 372469 },70 {71 "name": "dom-size",72 "value": 211073 },74 {75 "name": "estimated-input-latency",76 "value": 85977 },78 {79 "name": "consistently-interactive",80 "value": 2260781 },82 {83 "name": "first-interactive",84 "value": 2124885 },86 {87 "name": "json_body_size_in_bytes",88 "value": 290189 },90 {91 "name": "json_size_in_bytes",92 "value": 174793 },94 {95 "name": "image_body_size_in_bytes",96 "value": 86376597 },98 {99 "name": "image_size_in_bytes",100 "value": 863790101 },102 {103 "name": "font_body_size_in_bytes",104 "value": 402256105 },106 {107 "name": "font_size_in_bytes",108 "value": 404248109 },110 {111 "name": "js_body_size_in_bytes",112 "value": 1815834113 },114 {115 "name": "js_size_in_bytes",116 "value": 425500117 },118 {119 "name": "css_body_size_in_bytes",120 "value": 1329225121 },122 {123 "name": "css_size_in_bytes",124 "value": 93630125 },126 {127 "name": "html_body_size_in_bytes",128 "value": 425563129 },130 {131 "name": "html_size_in_bytes",132 "value": 42739133 },134 {135 "name": "page_wait_timing",136 "value": 1740137 },138 {139 "name": "page_size_in_bytes",140 "value": 2530039141 },142 {143 "name": "page_body_size_in_bytes",144 "value": 5537450145 },146 {147 "name": "asset_count",148 "value": 59149 },150 {151 "name": "onload",152 "value": 22592153 },154 {155 "name": "oncontentload",156 "value": 12595157 }158 ],159 "budget_alerts": null,160 "artifacts": {161 "filmstrip": {162 "thumbs": [163 "https://calibre-screenshots-prod.s3.amazonaws.com/05efb55f-0d2b-4c25-afd4-06dd7038fada/video-timeline/1682063.892.jpg",164 "https://calibre-screenshots-prod.s3.amazonaws.com/05efb55f-0d2b-4c25-afd4-06dd7038fada/video-timeline/1682532.696.jpg",165 "https://calibre-screenshots-prod.s3.amazonaws.com/05efb55f-0d2b-4c25-afd4-06dd7038fada/video-timeline/1685799.232.jpg",166 "https://calibre-screenshots-prod.s3.amazonaws.com/05efb55f-0d2b-4c25-afd4-06dd7038fada/video-timeline/1686499.204.jpg",167 "https://calibre-screenshots-prod.s3.amazonaws.com/05efb55f-0d2b-4c25-afd4-06dd7038fada/video-timeline/1686899.188.jpg",168 "https://calibre-screenshots-prod.s3.amazonaws.com/05efb55f-0d2b-4c25-afd4-06dd7038fada/video-timeline/1687299.172.jpg",169 "https://calibre-screenshots-prod.s3.amazonaws.com/05efb55f-0d2b-4c25-afd4-06dd7038fada/video-timeline/1688782.446.jpg",170 "https://calibre-screenshots-prod.s3.amazonaws.com/05efb55f-0d2b-4c25-afd4-06dd7038fada/video-timeline/1690182.39.jpg",171 "https://calibre-screenshots-prod.s3.amazonaws.com/05efb55f-0d2b-4c25-afd4-06dd7038fada/video-timeline/1691349.01.jpg",172 "https://calibre-screenshots-prod.s3.amazonaws.com/05efb55f-0d2b-4c25-afd4-06dd7038fada/video-timeline/1692448.966.jpg",173 "https://calibre-screenshots-prod.s3.amazonaws.com/05efb55f-0d2b-4c25-afd4-06dd7038fada/video-timeline/1693715.582.jpg",174 "https://calibre-screenshots-prod.s3.amazonaws.com/05efb55f-0d2b-4c25-afd4-06dd7038fada/video-timeline/1703281.866.jpg",175 "https://calibre-screenshots-prod.s3.amazonaws.com/05efb55f-0d2b-4c25-afd4-06dd7038fada/video-timeline/1703365.196.jpg",176 "https://calibre-screenshots-prod.s3.amazonaws.com/05efb55f-0d2b-4c25-afd4-06dd7038fada/video-timeline/1703381.862.jpg"177 ],178 "video": "https://calibre-screenshots-prod.s3.amazonaws.com/05efb55f-0d2b-4c25-afd4-06dd7038fada/video-timeline/screencast.mp4"179 },180 "har": "https://calibre-screenshots-prod.s3.amazonaws.com/5e7ebd8f932d99e2a017fd1e16551221/https___www_apple_com_iphone_x_/har/20180716233507265.json?X-Amz-Expires=3600&X-Amz-Date=20180716T233521Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAITFRYATLYAH7W3VQ/20180716/us-east-1/s3/aws4_request&X-Amz-SignedHeaders=host&X-Amz-Signature=be3e0818f2b8c24114d2813faf83b11e3a6d6b8084c84fb44ecde27104215045"181 }182 },183 {184 "uuid": "xxx-123-456-789-xxx",185 "name": "iPhone 6",186 "status": "completed",187 "endpoint": "https://www.apple.com/iphone-6/",188 "canonical": false,189 "profile": "iPhone 6, 3G connection",190 "profile_uuid": "xxx-123-456-789-xxx",191 "metrics": [192 {193 "name": "speed_index",194 "value": 8517195 },196 {197 "name": "visually_complete",198 "value": 11477199 },200 {201 "name": "lighthouse-seo-score",202 "value": 68203 },204 {205 "name": "lighthouse-best-practices-score",206 "value": 68207 },208 {209 "name": "lighthouse-accessibility-score",210 "value": 93211 },212 {213 "name": "lighthouse-performance-score",214 "value": 26215 },216 {217 "name": "lighthouse-pwa-score",218 "value": 36219 },220 {221 "name": "js-parse-compile",222 "value": 1501223 },224 {225 "name": "time-to-first-byte",226 "value": 108227 },228 {229 "name": "first-contentful-paint",230 "value": 3470231 },232 {233 "name": "first-meaningful-paint",234 "value": 8449235 },236 {237 "name": "firstRender",238 "value": 2398239 },240 {241 "name": "dom-size",242 "value": 727243 },244 {245 "name": "estimated-input-latency",246 "value": 17247 },248 {249 "name": "consistently-interactive",250 "value": 24364251 },252 {253 "name": "first-interactive",254 "value": 11378255 },256 {257 "name": "json_body_size_in_bytes",258 "value": 1716259 },260 {261 "name": "json_size_in_bytes",262 "value": 1011263 },264 {265 "name": "image_body_size_in_bytes",266 "value": 1968564267 },268 {269 "name": "image_size_in_bytes",270 "value": 1973146271 },272 {273 "name": "font_body_size_in_bytes",274 "value": 346672275 },276 {277 "name": "font_size_in_bytes",278 "value": 348381279 },280 {281 "name": "js_body_size_in_bytes",282 "value": 1077849283 },284 {285 "name": "js_size_in_bytes",286 "value": 271680287 },288 {289 "name": "css_body_size_in_bytes",290 "value": 720939291 },292 {293 "name": "css_size_in_bytes",294 "value": 62227295 },296 {297 "name": "html_body_size_in_bytes",298 "value": 51148299 },300 {301 "name": "html_size_in_bytes",302 "value": 9958303 },304 {305 "name": "page_wait_timing",306 "value": 198307 },308 {309 "name": "page_size_in_bytes",310 "value": 2666403311 },312 {313 "name": "page_body_size_in_bytes",314 "value": 4166888315 },316 {317 "name": "asset_count",318 "value": 73319 },320 {321 "name": "onload",322 "value": 28932323 },324 {325 "name": "oncontentload",326 "value": 7034327 }328 ],329 "budget_alerts": null,330 "artifacts": {331 "filmstrip": {332 "thumbs": [333 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1683338.5.jpg",334 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1683999.304.jpg",335 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1685749.234.jpg",336 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1686815.858.jpg",337 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1686882.522.jpg",338 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1688649.118.jpg",339 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1689749.074.jpg",340 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1691798.992.jpg",341 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1693432.26.jpg",342 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1693732.248.jpg",343 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1694015.57.jpg",344 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1694032.236.jpg",345 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1694098.9.jpg",346 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1694315.558.jpg",347 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1694498.884.jpg",348 "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/1694815.538.jpg"349 ],350 "video": "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/screencast.mp4",351 "gif": "https://calibre-screenshots-prod.s3.amazonaws.com/67ec59cf-0121-4ab7-ab38-e2f4459c40da/video-timeline/screencast.gif"352 },353 "har": "https://calibre-screenshots-prod.s3.amazonaws.com/f477b5ec59c5a357f1f7d756b23c4645/https___www_apple_com_iphone_6_/har/20180716233446500.json?X-Amz-Expires=3600&X-Amz-Date=20180716T233521Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAITFRYATLYAH7W3VQ/20180716/us-east-1/s3/aws4_request&X-Amz-SignedHeaders=host&X-Amz-Signature=86e1fe079e3ce90c81bf81a62369c71c03c31640412cd22dce92c1996f4247b7"354 }355 }356 ]357}
1{2 "id": "abc123",3 "url": "http://calibreapp.com/teams/your-team/your-site/budgets/abc123",4 "organisation_id": "your-organisation",5 "site_id": "your-site",6 "name": "Largest Contentful Paint",7 "abbreviated_name": "LCP",8 "value": 3000,9 "formatted": "3s",10 "status": "At risk",11 "threshold": "GreaterThan",12 "generated_at": "2020-09-30T05:33:17.970Z",13 "budgets": [14 {15 "profile": {16 "id": "pr123",17 "name": "Chrome Desktop"18 },19 "page": {20 "id": "p123",21 "name": "Marketing Home",22 "previews": []23 },24 "status": "At risk",25 "value": 2850,26 "formatted": "2.85s"27 },28 {29 "profile": {30 "id": "pr456",31 "name": "MotoG4, 3G connection"32 },33 "page": {34 "id": "p123",35 "name": "Marketing Home",36 "previews": []37 },38 "status": "Met",39 "value": 2400,40 "formatted": "2.4s"41 },42 {43 "profile": {44 "id": "pr789",45 "name": "iPhone, 4G LTE"46 },47 "page": {48 "id": "p123",49 "name": "Marketing Home",50 "previews": []51 },52 "status": "Met",53 "value": 2400,54 "formatted": "2.4s"55 }56 ]57}
Webhooks can be securely validated using the Calibre-HMAC-SHA256-Signature header. This header should be used in order to establish that the incoming request originates from Calibre.
This header is formulated by supplying a shared secret with Calibre.
Calibre uses HMAC cryptographic hash signature for verification purposes.
In order to verify the Calibre request, you will need to use the shared secret that you supplied earlier.
1// Example built for express.js, using buffer-equal-constant-time2// Requires environment variable containing the secret: process.env.SECRET_TOKEN34const crypto = require('crypto')5const bufferEq = require('buffer-equal-constant-time')67const verifyCalibreHMAC = (req, res, next) => {8 const payload = req.body()9 const payloadSignature = req.header('Calibre-HMAC-SHA256-Signature')1011 let hmac = crypto.createHmac('sha256', process.env.SECRET_TOKEN)12 hmac.write(payload)13 hmac.end()1415 const signature = hmac.read()1617 if (bufferEq(new Buffer(signature), new Buffer(payloadSignature))) {18 next()19 } else {20 throw new Error('HMAC Signatures do not match')21 }22}2324// Express will downcase headers25app.use(verifyCalibreHMAC)26app.post('/calibre-webhook', (req, res) => {27 res.send('We have a valid hmac signature')28})
1// SECRET_TOKEN is an environment variable23post '/calibre-webhook' do4 request.body.rewind5 payload_body = request.body.read6 verify_signature(payload_body)78 # Safely use the JSON9end1011def verify_signature(payload_body)12 signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV['SECRET_TOKEN'], payload_body)13 return halt 500, "Signatures didn't match!" unless ActiveSupport::SecurityUtils.secure_compare(signature, request.env['Calibre-HMAC-SHA256-Signature'])14end
Notes:
We recommend that you use a third party webhook test service, or enable verbose logging in order to verify the webhooks are delivered in the format as expected.