NAV Navbar
PHP Python
  • Introduction
  • Receiving webhooks
  • Timing
  • Introduction

    If you have an external system which you need to be kept in sync with your Observer project data, webhooks are for you. After changes are made to designs in Observer, our servers will send requests to your specified webhook URL with the new data.

    You'll need to implement your own HTTP endpoint that can receive updates from Observer's webhooks, or have some system that can accept our webhooks and act on the responses sensibly. The ideal scenario is that your own development team or IT contractors would implement the system and cater it to your business's needs.

    Setting up webhooks

    Visit your webhook integration settings page and enter the following information:

    Webhook URL

    Enter the URL where you would like HTTP requests to be made when your projects are edited. This will probably be a URL that is part of your CRM system's API.

    Webhook secret

    This parameter is optional. If it is set, each request made to your webhook URL will contain a header that you can use to verify the request. Adding a secret allows you to verify that requests made to your webhook endpoint (which must be publicly accessible) did in fact come from Observer. See Security below to learn how to implement secret-based verification.

    Receiving webhooks

    Request format

    An example webhook payload (see API documentation for details of projects)

    {
      "team": {
        "uid": "eDnP83Uj",
        "name": "My Solar Co"
      },
      "projects": [
        {
          "uid": "ndd1MMGi",
          "reference_number": "Q142",
          ...
          "designs": [
            {
              "uid": "iCbguJJc",
              ...
              "summary": {
                ...
              }
            }
          ]
        },
        {
          "uid": "TQ3YtwhB",
          ...
        }
      ]
    }
    

    Webhooks are sent to your URL as an HTTP POST request. The request body will be a JSON array containing updates for all projects. An example with details omitted is on the right:

    Parameter Type Description
    team.uid string The unique ID of the team that these projects belong to
    team.name string The name of the team these projects belong to
    projects array A list of Projects which have been edited since last update

    The contents of each project sent as part of the webhook update is identical to the Projects JSON API. See the API documentation for details.

    The team details are included in case you reuse the same webhook URL for multiple Observer teams.

    Security

    Example of receiving a webhook request and verifying its signature:

    <?php
    
    $secret = '...'; // Fill in with same secret used in webhook settings
    $request_body = file_get_contents('php://input');
    $request_signature = $_SERVER['HTTP_X_PYLON_OBSERVER_SIGNATURE'];
    $request_timestamp = $_SERVER['HTTP_X_PYLON_OBSERVER_TIMESTAMP'];
    $expected_signature = hash_hmac('sha256', $request_body . $request_timestamp, $secret);
    
    if ($request_signature === $expected_signature) {
        // Request is genuine
        $data = json_decode($request_body);
        // TODO: check that $request_timestamp is within a minute of now
    }
    
    # Assume we're in a CGI script environment
    import os, sys, hashlib, hmac, json
    
    secret = '...' # Fill in with secret from webhook creation
    request_body = sys.stdin.read()
    request_signature = os.environ['HTTP_X_PYLON_OBSERVER_SIGNATURE']
    request_timestamp = os.environ['HTTP_X_PYLON_OBSERVER_TIMESTAMP']
    expected_signature = hmac.new(secret, request_body + request_timestamp, hashlib.sha256).hexdigest()
    
    if request_signature == expected_signature:
        # Request is genuine
        data = json.loads(request_body)
        # TODO: check that request_timestamp is within a minute of now
    

    If you want to verify that webhook payloads are actually coming from Observer and not a third party who has discovered your endpoint, you must provide a secret when creating your webhook. A webhook secret should be a random string of at least 20 characters. Each webhook request contains a digest header which is created by hashing the webhook request content and the time the request was sent with this secret. To verify that the request is from Observer, you can perform the check yourself and verify that the digest matches what you expect.

    The timestamp header allows you to reject old requests being played back by an attacker; simply reject requests with a timestamp that is more than a minute from the current time.

    The digest is provided in the header X-Pylon-Observer-Signature. The request timestamp is provided in the header X-Pylon-Observer-Timestamp. The digest is created using the following algorithm:

    signature = sha256(request_body + request_timestamp, secret)

    Timing

    Webhooks are not run instantly when changes are made. This is because Observer contains many asynchronous processes, such as rendering snapshots and performing calculations. Webhooks are updated every 60 seconds, and projects may appear in multiple updates as changes are made asynchronously.