Event service documentation (Webhooks)

How to receive event payloads from the platform

Last updated on February 28th, 2024

Changelog

January 2024

Design & API changes

  • Webhooks are no longer limited to only 1 URL for the entire setup. Instead, every webhook can have its own URL.
  • Webhooks now have their own dedicated API endpoint – /api/sales-partners/webhooks

New webhooks

We have introduced new webhooks explained in 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 and the array newDataPointIds lists the ids of each newly scraped datapoint. The same logic is applicable for BRAND_DATAPOINT_CHECK

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
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