Event service documentation (Webhooks)

    How to receive event payloads from the platform

    Last updated on July 17th, 2024

    Changelog

    January 2024

    Design & API changes

    • Webhooks are no longer limited to only 1 URL for the entire configuration. Instead, every webhook can have its specific destination URL.
    • Webhooks now have their dedicated API endpoint – /api/sales-partners/webhooks
      • Previously, they were set by the PUT /api/sales-partners/$id endpoint and passing the pushUrl and subscribedWebhookEventTypes which is no longer supported.

    New webhooks

    We have introduced new webhooks explained in the Event types definitions section.

    • LISTING_DATAPOINT_INVALID
    • BRAND_DATAPOINT_CHECK
    • DIRECTORY_BUSINESS_PAGE_DATA_POINT_INVALID
    • LOCATION_DATA_SUGGESTION

    Existing webhook changes

    We have added parameters to streamline the handling of data points. 

    LISTING_DATAPOINT_CHECK – when you listen for new data points, now you also receive which are the new datapoints so that they can be directly queried after

    • The boolean field newDatapointsFound indicates if any new data point has been found
    • The array newDataPointIds lists the ids of each newly scraped datapoint. The same logic is applicable for BRAND_DATAPOINT_CHECK
    • The array updatedDataPointIds will contain information for existing data points that have been updated, e.g. the end user has modified a customer review.

    What is the event service? What are webhooks?

    Webhooks are automated messages sent from apps when something happens. The platform's Event Service (Webhooks) allows enables real-time communication between our platform and other applications. It works by sending HTTP POST requests to a provided URL for subscribed events, such as changes in location information, updates to business listings, or modifications to social media interactions (e.g. reviews). This seamless integration allows you to stay informed about dynamic changes and facilitates the transfer of real-time data.

    It can help you get real-time pings when and only a change happens

    Setting up the service

    The service can be set up in two ways:

    Set up via the platform's interface

    This is the most straightforward way. Log in with an Admin user in the platform, navigate to Org settings and you should see Webhooks tab. There you can add a designated target URL, per webhook, and also see the available webhooks.

    API set up

    Webhooks can be also set up by the API. 

    Supported webhook types

    We offer various webhooks explained further below. Based on your use cases, you may decide to consume one or multiple webhooks.

    To understand which are the currently supported event types (webhooks) you can query the following endpoint:

    curl --location 'https://$yourdomain.com/api/sales-partners/subscribable-event-types' \
    --header 'privateKey: $privateKey'

    Subscribing to a webhook

    To set up a new webhook you can run the following query. We allow sending the same webhook to multiple destinations (pushUrl

    curl --location 'https://$yourdomain.com/api/salespartners/webhooks' \
    --header 'privateKey: $privateKey' \
    --data '{
        "pushUrl" : "https://webhook.site/e84ba9cc-d56f-4e30-9607-cd3ddc65952f",
        "type" : "LISTING_DATAPOINT_CHECK"
    }'

    Event types definitions

    The application offers the following events (webhooks):

    Type

    Description

    LISTING_SYNC_CHECK a listing was sync checked, including possible status listing changes
    LISTING_LINK_CHANGE the url of a listing has changed
    LISTING_UPDATE listing's data was attempted to be submitted to the directory
    LISTING_STATUS_CHANGE Indicates that the status of a listing was changed manually. This happens rarely and often is the result of a manual check by our Operations or Engineering teams.
    LISTING_DATAPOINT_CHECK New datapoint(s) for a listing have been found or changes to existing datapoints have occurred
    LISTING_DATAPOINT_INVALID a listing datapoint has been deleted from the directory so we have invalidated it, too
    DIRECTORY_BUSINESS_PAGE_DATA_POINT_CHECK new datapoint(s) for a brand page been found
    DIRECTORY_BUSINESS_PAGE_DATA_POINT_INVALID a brand datapoint has been deleted from the directory so we have invalidated it, too
    LOCATION_CREATED a location has been created
    LOCATION_STATUS_CHANGED a location's status has changed 
    LOCATION_PROFILE_CHANGED a location has been updated by a user
    BUSINESS_PRODUCT_PLAN_CHANGED a business (and its subsequent locations) has changed product plans
    BUSINESS_CREATED a business has been created

    The target URL and the subscribed events can be changed during runtime. In case the transmission of an event fails, the system will retry a few times and then discard the event.

    What a payload looks like

    Here's the basic structure of a payload. More information about the different event types and loads can be found in the Field Descriptions and Events.

    {
      "event": {
        "comment": "Location profile has been updated",
        "id": 2926113683,
        "location": 636605,
        "time": "2018-11-05T17:35:07.000+01:00",
        "type": "LOCATION_PROFILE_CHANGED",
        "user": "user@test.com"
      },
      "signature": "X"
    }

    All JSON payloads will always have the event and signature fields.

    Field Description

    Event

    The event represents the change that has happened in the platform and it serves as the actual payload and varies based on the event type. Key fields include but are not limited to:

    Field Description Comment
    id unique id of the event being sent can be used to make sure no events ever are handled twice
    time the time the event occurred  
    type the type of events see below description for possible values
    listing the id of the listing to which the event is related  
    location the id of the location to which the event is related can be used to react to the event and e.g. get the new location data after a change
    business the id of the business to which the event is related  can be used to react to the event and e.g. get the new business after its creation
    salesPartnerIdentifier the identifier of the partner account can be used to react to the event and e.g. identify in which account the object was created (if you manage several)

    All other fields are different per event type and described further down.

    Signature

    The signature is calculated by casting the event field to a String and then HMAC_SHA256 encrypted using the sales partner's private key and BASE64 encode the result. The events fields need to be ordered alphabetically before decoding!

    Pseudo Code:

    let json = {"asd": "qwe", "foo": "bar"};
    let str = json.encodeAsJson();
    assert str === '{"asd": "qwe", "foo": "bar"}';

    let privateKey = "this-is-a-secret-key"
    let encrypted = hmacShaEncrypt(privateKey, str)
    let signature = encrypted.encodeAsBase64()
    assert signature === 'UAQuAderRxKW8nMPhyU8oVhS6U8PgG8d/I03lcbNAG4='

     

    Events

    Below is the list of the supported events. We continue 

    LISTING_SYNC_CHECK

    Indicates that any of the three main listing statuses (Claim, Flow, Sync) changed. It includes information about the lifecycle of the listing and the Google status of the listing.

    {
      "signature": "X",
      "event": {
        "claimStatusChanged": false,
        "googleStatusChanged": false,
        "directoryType": "ABCLOCAL",
        "flowStatusChanged": true,
        "fromListingClaimStatus": "CLAIMED_BY_US",
        "fromListingFlowStatus": "ALL_INFORMATION_SUBMITTED",
        "fromListingSyncStatus": "NOT_IN_SYNC",
        "id": 213581003,
        "listing": 332874,
        "location": 34838,
        "syncStatusChanged": true,
        "time": "2016-05-03T10:42:06.000+02:00",
        "toListingClaimStatus": "CLAIMED_BY_US",
        "toListingFlowStatus": "NO_ACTION_NEEDED",
        "toListingSyncStatus": "IN_SYNC",
        "type": "LISTING_SYNC_CHECK"
      }
    }

     

    Field

    Type

    Description

    directoryType string the directory this listing belongs to
    claimStatusChanged boolean indicating whether the listing's claim status has changed
    googleStatusChanged
     
    boolean indicating whether the listing's Google connection status has changed
    flowStatusChanged boolean indicating whether the listing's flow status changed
    syncStatusChanged boolean indicating whether the listing's sync status changed
    fromListingClaimStatus ListingClaimStatus the previous ClaimStatus
    fromListingFlowStatus FlowStatus the previous FlowStatus
    fromListingSyncStatus SyncStatus the previous SyncStatus
    toListingClaimStatus ListingClaimStatus the new ClaimStatus
    toListingFlowStatus FlowStatus the new FlowStatus
    toListingSyncStatus SyncStatus the new SyncStatus
     
     
     

    LISTING_STATUS_CHANGE

    Indicates that the status of a listing was changed manually. This happens rarely and often is the result of a manual check by our Operations or Engineering teams.

    {
      "signature": "X",
      "event": {
        "claimStatusChanged": false,
        "directoryType": "ABCLOCAL",
        "flowStatusChanged": true,
        "fromListingClaimStatus": "CLAIMED_BY_US",
        "fromListingFlowStatus": "NO_ACTION_NEEDED",
        "fromListingSyncStatus": "NOT_IN_SYNC",
        "id": 213581003,
        "listing": 332874,
        "location": 34838,
        "syncStatusChanged": false,
        "time": "2016-05-03T10:42:06.000+02:00",
        "toListingClaimStatus": "CLAIMED_BY_US",
        "toListingFlowStatus": "SUBMISSION_NEEDED",
        "toListingSyncStatus": "NOT_IN_SYNC",
        "type": "LISTING_STATUS_CHANGE",
        "user": "user@example.com"
    
      }
    }

     

    Field Type Description

    directoryType

    string

    the directory this listing belongs to

    claimStatusChanged

    boolean

    indicating whether the claim status changed

    flowStatusChanged

    boolean

    indicating whether the flow status changed

    syncStatusChanged

    boolean

    indicating whether the sync status changed

    fromListingClaimStatus

    ListingClaimStatus

    the previous ClaimStatus

    fromListingFlowStatus

    FlowStatus

    the previous FlowStatus

    fromListingSyncStatus

    SyncStatus

    the previous SyncStatus

    toListingClaimStatus

    ListingClaimStatus

    the new ClaimStatus

    toListingFlowStatus

    FlowStatus

    the new FlowStatus

    toListingSyncStatus

    SyncStatus

    the new SyncStatus

    user

    string

    the user that changed the status

     
     
     

    LISTING_LINK_CHANGE

    The platform listing is now pointing to a different directory listing. This can happen either manually or automatically, by detecting a better listing on the directory side than the one previously linked.

    {
        "event": {
            "business": 1234,
            "comment": "Listing link changed",
            "fromListingId": "456",
            "fromListingUrl": "http://old-listing.url/456",
            "id": 5549734563,
            "listing": 2345,
            "location": 3456,
            "time": "2019-11-18T14:12:36.000+01:00",
            "toListingId": "789",
            "toListingUrl": "http://new-listing.url/789",
            "type": "LISTING_LINK_CHANGE"
        },
        "signature": "X"
    }

     

    Field

    Type

    Description

    fromListingUrl string the old listing url
    fromListingId string the old external id of the listing
    toListingUrl string the new listing url
    toListingId string the new external id of the listing
     
     
     

    LISTING_DATAPOINT_CHECK

    Indicates that while doing a check for listing datapoints (i.e. customer feedback such as reviews, photos, etc.), we found new ones.

    {
      "event": {
        "business": 1420951,
        "directoryType": "JUDYS_BOOK"
        "id": 213580990,
        "listing": 332874,
        "location": 34838,
        "newCheckinsFound": 1,
        "newCommentsFound": 1,
        "newConversationsFound": 1,
        "newDatapointsFound": true,
        "newPhotosFound": 1,
        "newQuestionsFound": 1,
        "newReviewsFound": 1,
        "newDataPointIds": [
          250976288,
          250976289
        ],
        "time": "2016-05-03T10:19:14.000+02:00",
        "type": "LISTING_DATAPOINT_CHECK"
      },
      "signature": "X"
    }

     

    Field

    Type

    Description

    directoryType string

    the directory this listing belongs to

    newPhotosFound

    integer

    the number of new photos found

    newReviewsFound

    integer

    the number of new reviews found

    newCheckinsFound

    integer

    the number of new checkins found

    newConversationsFound

    integer

    the number of new conversations found

    newCommentsFound

    integer

    the number of new comments found

    newQuestionsFound

    integer

    the number of new questions found

    newDatapointsFound boolean indicates if any new data points have been fond
    newDataPointIds array contains the ids of the newly found data points
    updatedDataPointIds array contains the ids of any existing and modified data points (e.g. customer review)
     
     
     

    LISTING_DATAPOINT_INVALID

    Indicates that a listing's datapoint has been deleted or invalidated on the directory, so we invalidate it on our side. You can delete or invalidate them on your side too. It's unlikely that a datapoint will be reinstated by the directory.

    {
      "event": {
        "business": 1107630,
        "deletedDataPointIds": [],
        "directoryType": "YELP_API",
        "id": "99",
        "invalidDataPointIds": [
          434424555
        ],
        "listing": 119934719,
        "location": 4154762,
        "time": "2023-12-20T19:00:34.508+01:00",
        "type": "LISTING_DATAPOINT_INVALID"
      },
      "signature": "YIfeSTln/p2JaPTY3WhVSsFRvy3NgtRhCFzGI/Fc+60="
    }

    Field

    Type

    Description

    deletedDataPointIds Array The ids of the invalidated data point replies (e.g. owner reply to a customer review)
    invalidDataPointIds Array

    The ids of the invalidated data points (e.g. customer review)

     
     

    DIRECTORY_BUSINESS_PAGE_DATA_POINT_CHECK

    Indicates that a new brand data point has been found, e.g. review on a Facebook brand page

    {
      "event": {
        "business": 244397,
        "directoryBusinessPage": 1220,
        "directoryType": "FACEBOOK",
        "id": 4,
        "newDataPointIds": [
          289,
          299
        ],
        "newDatapointsFound": true,
        "time": "2023-12-21T12:03:31.358+01:00",
        "type": "DIRECTORY_BUSINESS_PAGE_DATA_POINT_CHECK",
        "updatedDataPointIds": [
          79,
          253
        ]
      },
      "signature": "9kTmfuFg9T5LDD59ZodiyOFtoLSItG737ZRL2fBD7O4="
    }

    Field

    Type

    Description

    directoryBusinessPage Integer The id of the business page associated with the brand data point
    newDataPointIds Array The ids of the newly found brand data points
    updatedDataPointIds Array

    contains the ids of any existing and modified data points  (e.g. customer review)

     
     

     

    DIRECTORY_BUSINESS_PAGE_DATA_POINT_INVALID

    Indicates that a brand's datapoint has been deleted or invalidated on the directory, so we invalidate it on our side. You can delete or invalidate them on your side too. It's unlikely that a datapoint will be reinstated by the directory.

    {
      "event": {
        "business": 909288,
        "deletedDataPointIds": [],
        "directoryBusinessPage": 173,
        "directoryType": "FACEBOOK",
        "id": 7,
        "invalidDataPointIds": [
          4737279
        ],
        "time": "2024-01-02T17:17:49.664+01:00",
        "type": "DIRECTORY_BUSINESS_PAGE_DATA_POINT_INVALID"
      },
      "signature": "/GHkwuBIxYadsaWPq1V8vztXVuSlf1txDSxZuBAH1UQ="
    }

    Field

    Type

    Description

    directoryBusinessPage Integer The id of the business page associated with the brand data point
    deletedDataPointIds Array The ids of the invalidated data point replies (e.g. owner reply to a customer review)
    invalidDataPointIds Array

    The ids of the invalidated data points (e.g. customer review)

     
     

    LISTING_UPDATE

    Indicates that we tried to submit the listing to the directory.

    {
     "event": {
     "business": 7141,
     "directoryType": "BING",
     "id": 17518447719,
     "listing": 7198166,
     "location": 450978,
     "messages": [
     "Listing was updated successfully!"
     ],
     "time": "2021-10-27T16:21:14.000+02:00",
     "type": "LISTING_UPDATE",
     "updateResult": "SUCCESS",
     "updateStatus": null
     },
     "signature": "X"
    }

    Field

    Type

    Description

    directoryType string The directory's listing type which we tried to submit  

    updateResult

    string

    • SUCCESS - listing has been successfully updated
    • FAILURE - listing update was rejected by directory
    • UNDEFINED - listing data could not be submitted due to some reason outlined in UpdateStatus below
    updateStatus string

    null if updateResult = SUCCESS. Else can be one of:

            MANDATORY_FIELD_MISSING, // one or more fields are mandatory, but are not set in the location
            NOT_SUPPORTED, // the listing is not supported on that directory (e.g. INSTAGRAM -> we only have listing URLs without any direct submission)
            INVALID_CLAIM_STATUS, // claim status is invalid (e.g. CLAIMED_BY_OTHERS, but we can only update CLAIMED_BY_US)
            INVALID_FLOW_STATUS, // flow status is invalid (e.g. NEEDS_REVIEW)
            INVALID_SYNC_STATUS, // sync status is invalid (e.g we cannot update listing that have TECHNICAL_PROBLEMS)
            INVALID_LOCATION_STATUS, // location status is invalid (e.g. NEEDS_REVIEW)
            LOCATION_NOT_NORMALIZED, // location is not normalized
            DUPLICATED, // listing is duplicated, we should not display the logging event as a failure, as there's been no error. see CylexUpdateService#update
            LIMIT_REACHED, // can be used when the update fails for throttling or for limits set by the directory
            ADDRESS_DISPLAY_NOT_SUPPORTED, // directory doesn't support address display
            LOCATION_NOT_SYNCED, // the location of the listing has not been synced yet
            LOCATION_NOT_CLEANSED, // the location has not been cleansed yet so we shouldn't push data to some directories that require cleansing-only
            DELAYED, // the sync was delayed to a later point in time because of directory restrictions
     
     

    LOCATION_CREATED

    Indicates that a new location has been created in your account.

    {
      "event": {
        "comment": "A new Location has been created",
        "id": 2926113683,
        "location": 636605,
        "time": "2018-11-05T17:35:07.000+01:00",
        "type": "LOCATION_CREATED",
        "source": "API"
      },
      "signature": "X"
    }

     

    Field

    Type

    Description

    source

    string

    one among [API, CHECKOUT, CSV_UPLOAD, DEMO_JOB, LOCATION_DATA_DOWNLOAD]

     
     
     

    LOCATION_STATUS_CHANGED

    Indicates that the status of a location changed.

    {
      "event": {
        "comment": "Checked and activated by a human being.",
        "from": "ACTIVE",
        "id": 213580999,
        "location": 34838,
        "time": "2016-05-03T10:34:43.000+02:00",
        "to": "CANCELLED",
        "type": "LOCATION_STATUS_CHANGED",
        "user": "user@example.com"
      },
      "signature": "X"
    }

     

    Field

    Type

    Description

    from LocationStatus the previous LocationStatus
    to LocationStatus the new LocationStatus
    user string the user who changed the status (if it was a manual change)
     
     
     

    LOCATION_PROFILE_CHANGED

    Indicates that a user changed data of a location.

    {
      "event": {
        "comment": "Location profile has been updated",
        "id": 2926113683,
        "location": 636605,
        "time": "2018-11-05T17:35:07.000+01:00",
        "type": "LOCATION_PROFILE_CHANGED",
        "user": "user@example.com"
      },
      "signature": "X"
    }

     

    Field

    Type

    Description

    user string the user that changed the data
     
     
     

    BUSINESS_PRODUCT_PLAN_CHANGED

    Indicates that a user and API update or our system has changed the ProductPlan of a Business and its assigned Locations

    {
        "event": {
            "business": 1,
            "comment": "Business product plan has been changed",
            "id": 5136210330,
            "salesPartnerIdentifier": "identifier",
            "newProductPlanId": 2,
            "newProductPlanName": "My Product Plan 2",
            "newProductPlanPrice": 20,
            "oldProductPlanId": 1,
            "oldProductPlanName": "My Product Plan 1",
            "oldProductPlanPrice": 10,
            "time": "2019-10-09T15:10:01.000+02:00",
            "type": "BUSINESS_PRODUCT_PLAN_CHANGED",
            "changedBy": "user@example.com"
        },
        "signature": "XYZ"
    }

     

    Field

    Type

    Description

    newProductPlanId Long the id of newly assigned ProductPlan
    newProductPlanName String the name of the newly assigned ProductPlan
    newProductPlanPrice Integer the price (in Cent) of newly assigned ProductPlan
    oldProductPlanId Long the id of the previously assigned ProductPlan
    oldProductPlanName String the name of the previously assigned ProductPlan
    oldProductPlanPrice Integer the price (in Cent) of the previously assigned ProductPlan
    changedBy String Either one of the following, indicating who triggered the change:
    1. user email
    2. “PRIVATE_TOKEN”
    3. “SYSTEM”
     
     
     

    BUSINESS_CREATED

    Indicates that a new Business has been created in the partner account

    {
        "event": {
            "business": 631498,
            "changedBy": "PRIVATE_TOKEN",
            "comment": "New business has been created",
            "id": 5385813249,
            "salesPartnerIdentifier": "identifier",
            "time": "2019-11-07T16:30:19.000+01:00",
            "type": "BUSINESS_CREATED"
        },
        "signature": "X”
    }


     

    Field

    Type

    Description

    changedBy String Either one of the following, indicating who triggered change:
    1. user email
    2. “PRIVATE_TOKEN”
    3. “SYSTEM”
     
     
     

     

     

     

     

    Was this article helpful?

    Save as PDF